ios下 KeyChain 的研究

iOS的keyChain是一个相对独立的空间,当我们的程序(App)被替换或者删除时并不会删除保存在keyChain的内容。相对于NSUserDefaults、plist文件保存等一般方式,keychain保存更为安全。所以我们会用keyChain保存一些私密信息,比如密码、证书、设备唯一码(UDID)等等。

我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。

数据结构如下:

对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区。私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见,其他应用程序无法访问该区数据。如果要想将存储的内容放在公共区,实现多个应用程序间可以共同访问一些数据,则可以先声明公共区的名称,官方文档管这个名称叫“keychain access group”。声明的方法是新建一个plist文件,名字随便起。如上:“YOUR_APP_ID_HERE.com.jaybin.keychain.test” 就是这个公共访问存储区的名称。

这里我们先介绍 KeyChain 私有区的访问存储,即该私有区只能给自己程序使用。比如保存用户密码,设备唯一码(UDID)等等。要在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻开发,我们可以使用一些已经封装好了的工具类。KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”这两个文件拷贝到项目,并导入Security.framework

1、保存账号信息到KeyChain:

/* 初始化一个保存用户帐号的 KeychainItemWrapper */
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"userAccount" accessGroup:nil];

    //向keychain里存储用户名、密码
    NSString *username = @"JayBin";
    NSString *password = @"123";
    [wrapper setObject:username forKey:(id)kSecAttrAccount];
    [wrapper setObject:password forKey:(id)kSecValueData];

标识符(Identifier)在后面我们要从keychain中取数据的时候会用到。如果你想要在应用之间共享信息,那么你需要指定访问组(keychain access group)。有同样的访问组的应用才能够访问同样的keychain信息。这里我们先介绍私有区,所以accessGroup选项填“nil”。需要注意的是方法 “- (void)setObject:(id)inObject forKey:(id)key;” 里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会崩溃。

通过上面的代码操作,我们已经将用户账号信息保存在该应用对应的keychain中,即使该应用被删除了,keychain中的账号信息也不会被删除。

2、从KeyChain获得存储的账号信息:

/* 初始化一个 KeychainItemWrapper */
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"userAccount" accessGroup:nil];
    username = [wrapper objectForKey:(id)kSecAttrAccount];
    password = [wrapper objectForKey:(id)kSecValueData];

    NSLog(@"name:%@",username);
    NSLog(@"password:%@",password);

注意,该KeychainItemWrapper 对象的标识符(userAccount)必须和我们上面保存账号信息的标示符一致。accessGroup选项还是填“nil”。当然wrapper对象对应的Key要对应上。

到这里我们还是具体了解一下钥匙串的基本数据结构。

钥匙串中的条目称为SecItem,但它是存储在CFDictionary中的。SecItemRef类型并不存在。SecItem有五类:通用密码、互联网密码、证书、密钥和身份。在大多数情况下,我们用到的都是通用密码。上面介绍的KeyChainItemWrapper也就是只使用通用密码。最后,我们需要在钥匙串中搜索需要的内容。密钥有很多个部分可用来搜索,但最好的办法是将自己的标识符赋给它,然后搜索。通用密码条目都包含属性kSecAttrGeneric,可以用它来存储标识符。这也是KeyChainItemWrapper的处理方式。

          如上图,每一个keyChain的组成如图,整体是一个字典结构.

1.kSecClass key 定义属于那一种类型的keyChain(通用密码、互联网密码、证书、密钥和身份)

2.不同的类型包含不同的Attributes,这些attributes定义了这个item的具体信息

3.每个item可以包含一个密码项来存储对应的密码

一般的使用方式如下:

/* 初始化一个 KeychainItemWrapper */
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"userAccount" accessGroup:nil];
    //设置keyChain的类型为通用密码
    [wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];
    //保存用户名
    [wrapper setObject:@"username" forKey:(id)kSecAttrAccount];
    //保存密码
    [wrapper setObject:@"password"forKey:(id)kSecValueData];
    //指定这个数据的访问权限,这里指定这个应用合适需要访问这个数据
    [wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];

当然我们想知道到底iOS系统和第三方应用在Keychain里都存放了哪些信息,我们可以使用Keychain dumper。目前从Keychain中导出数据的最流行工具是ptoomey3的Keychain dumper。详情可以参照此博文http://blog.csdn.net/yiyaaixuexi  

后面我们介绍Keychain的公共访问存储区,实现不同APP共享Keychain中的数据。

要实现多个应用程序间可以共同访问Keychain的一些数据,要先声明Keychain公共区的名称,官方文档管这个名称叫“keychain access group”。声明的方法是新建一个plist文件,名字随便起。如:“YOUR_APP_ID_HERE.com.jaybin.keychain.test” 就是这个公共访问存储区的名称。这里我们通过新版的Xcode可以简便很多。

Project->Capebilities->Keychain Sharing  ,将Keychain Sharing打开。如下图:

上图的keychain groups 填的就是这个公共访问存储区的名称 “YOUR_APP_ID_HERE.com.jaybin.keychain.test”。(当然,共享Keychain数据的多个应用设置的公共访问存储区名称应该是相同的)。我们可以看到下方显示两个“√”  ,如果是显示“×” 可以点击 fixit 。这是因为这个文件的路径要配置在 Project->build setting->Code Signing Entitlements
里,否则公共区无效。配置好后,须用你正式的证书签名编译才可通过。否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。在新版的Xcode中,将Keychain Sharing打开后,会在项目对应的目录下自动生成对应的Entitlements文件(一个plist文件)。如下图:

并且会在 Project->build setting->Code Signing Entitlements 里自动配置这个文件的路径。

到这里,我们基本上完成了Keychain的公共访问存储区“keychain access group”的设置部署了。需要注意的是,由于“keychain access group”配置好后,须用正式的证书签名编译才可通过。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。这里是指,相同bundle的应用程序通过设置 group 可以互相共享同组的keychain数据。相同bundle解释就是:比如这里有两个应用程序:
A应用程序使用的provision对应的 bundle id是 com.jaybin.keychain1,B应用程序使用的provision对应的 bundle id是 com.jaybin.keychain2 。那么这两个应用程序就可以共享keychain数据。下面以这两个应用程序为例。(这两个程序必须按照上面的步骤开启了Keychain Sharing,并且设置了相同的“keychain access group”)

应用程序A(bundle id是 com.jaybin.keychain1)向Keychain的公共访问存储区存入账号信息。

/* 初始化一个保存用户帐号的 KeychainItemWrapper */
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"userAccount" accessGroup:nil];

    //向keychain里存储用户名、密码
    NSString *username = @"JayBin";
    NSString *password = @"123";
    [wrapper setObject:username forKey:(id)kSecAttrAccount];
    [wrapper setObject:password forKey:(id)kSecValueData];

应用程序B(bundle id是 com.jaybin.keychain2)从Keychain的公共访问存储区中读取应用程序A存储的账号信息。

/* 初始化一个 KeychainItemWrapper */
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"userAccount" accessGroup:nil];
    username = [wrapper objectForKey:(id)kSecAttrAccount];
    password = [wrapper objectForKey:(id)kSecValueData];

    NSLog(@"name:%@",username);
    NSLog(@"password:%@",password);

需要注意的是在创建KeychainItemWrapper 对象时并没有指定特定的 group,因为在我们已经开启了Keychain Sharing并且设置了Entitlements,系统则会默认添加你keychain-access-groups里第一个group,即我们上面已经声明的group( “YOUR_APP_ID_HERE.com.jaybin.keychain.test”)。

时间: 2024-11-14 20:41:16

ios下 KeyChain 的研究的相关文章

iOS下ffmepg开发的一些参考资料汇总(陆续更新)

http://blog.csdn.net/sing_sing/article/details/8468798 近来在搞iOS下视频播放,研究了ffmpeg,现将看到比较好的资料汇总一下,方便自己或者有需要的人查阅. 1.iOS下ffmpeg的编译,网上有很多资料,但很多都有些问题的,找了几天,发现这个资料说得比较详细. http://www.cnblogs.com/ikodota/archive/2012/09/12/compile_ffmpeg_for_iOS.html 不过按这篇文章介绍的方

ios下(个人公司非企业级)AdHoc在线安装全环境配置

1,环境 客户端开发:MacOs 10.8.5 服务器开发:Centos6.3 64位 2,软件准备 Apache httpd 2.2.27 OpenSSL 0.9.8za 3,客户端准备 Apple的开发者账号大致分如下三类:个人,公司,企业,这一篇我们主要说下个人.公司的ipa在线安装.个人公司级别的AdHoc有这样一个限制:就是在线发布的ipa包只能安装 在添加到Apple账号的Devices列表中(发布IPA之后再添加的设备需要重新打IPA包)的非越狱设备及越狱设备中,而企业级的开发者账

李洪强iOS下的实际网络连接状态检测

iOS下的实际网络连接状态检测 序言 网络连接状态检测对于我们的iOS app开发来说是一个非常通用的需求.为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示.对绝大部分iOS开发者来说,从苹果示例代码改变而来的各种Reachablity框架是实现这个需求的普遍选择,比如这个库.但事实上,基于此方案的所有实现,都无法帮助我们检测真正的网络连接状态,它们能检测的只是本地连接状态:这种情况包括但不限于如下场景: 1.现在很流行的公用wifi,需要网页鉴权,鉴权之前无法

[转] ios中KeyChain用途

转自  http://www.2cto.com/kf/201311/255684.html 一.在应用间利用KeyChain共享数据 我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add. update.get.delete这四个操作.对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区.私有区是一个sandbox,本 程序存储的任何数据都对其他程序不可见.而要想在将存储的内容放在公共区,需要先

ios中KeyChain用途

一.在应用间利用KeyChain共享数据 我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add.update.get.delete这四个操作.对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区.私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见.而要想在将存储的内容放在公共区,需要先声明公共区的名称,官方文档管这个名称叫“keychain access group”,声明的方法

iOS多线程的初步研究(四)-- NSTimer

 iOS多线程的初步研究(四)-- NSTimer 原文地址  http://www.cnblogs.com/sunfrog/p/3243230.html 理解run loop后,才能彻底理解NSTimer的实现原理,也就是说NSTimer实际上依赖run loop实现的. 先看看NSTimer的两个常用方法: + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelec

ios 下使用ffmpeg随记

ffmpeg是一个多平台多媒体处理工具,处理视频和音频的功能非常强大.目前在网上搜到的iOS上使用FFMPEG的资料都比较陈旧,而FFMPEG更新迭代比较快: 且网上的讲解不够详细,对于初次接触FFMPEG的新手(例如我)来说确实不太好使用.为了防止忘记,这里对iOS下使用FFMPEG做一个总结. 1. FFMPEG层次结构的简单理解 要使用FFMPEG,首先需要理解FFMPEG的代码结构.根据志哥的提示,ffmpeg的代码是包括两部分的,一部分是library,一部分是 tool.api都是在

ios中KeyChain用途(整理)

原文地址http://blog.csdn.net/u011439689/article/details/18707387 一.在应用间利用KeyChain共享数据 我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add.update.get.delete这四个操作.对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区.私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见.而要想在将存

解决ios下的微信打开的页面背景音乐无法自动播放

后面的项目发现,还有两个坑,需要注意下: ·本文的解决方案的核心是利用了 微信/易信 在ready的时候会有个 WeixinJSBridgeReady/YixinJSBridgeReady事件,通过监听这个事件来触发的.那有个坑就是 如果微信已经ready了,但还没执行到你监听这个ready事件的代码,那么你的监听是没用的,所以最理想的情况是,监听的js放在head前面(放在css外链之前),确保最新执行,切记!切记!. ·另一个坑就是,本文的解决方案只适合一开始就播放的背景音乐.如果你是做那种