详细资料,请参看苹果官方文档Keychain Services Reference 。
ios中的keychain,用于保存用户的机密信息,对keychain的操作有4种,就是 增,删,改,查:
SecItemCopyMatching 查
Returns one or more keychain items that match a search query, or copies attributes of specific keychain items.
SecItemAdd 增
Adds one or more items to a keychain.
SecItemUpdate 改
Modifies items that match a search query.
SecItemDelete 删
Deletes items that match a search query.
每个操作,都需要定义相应的CFDictionary,下面看看如何定义。
操作函数需要的CFDictionary中的信息,介绍如下:
A dictionary containing an item class key-value pair (“Keychain Item Class Keys and Values” (page 11)) and optional attribute key-value pairs (“Attribute Item Keys and Values” (page 16)) specifying the item's attribute values.
上边提到了2种key-value pair,一种是 item class key-value pair,一种是optional attribute key-value pair,首先要确定item所用的class,之后根据不同的class会用到不同的attribute。
item class key-value pair,它被描述成Keychain Item Class Keys and Values。它的key是kSecClass ,它的value有5种:
kSecClassGenericPassword,//存储普通的密码, kSecClassInternetPassword,//存储网络密码 kSecClassCertificate,//存储证书,证书中包含共有密匙 kSecClassKey,//存储密匙,其实就是私有密匙 kSecClassIdentity,//存储Identity item,包括一个证书和一个密匙
这5种value都有相应的attribute的key-value pair 可以使用,请参看官方文档。这些attribute key-value pair 的key都是固定的常量; 他们的value,有的是常量, 有的是NSString之类的变量,可以写入自定义内容。另外,这些attribute key-value pair 有的指定了检索的条件,有的指定了检索的返回值,等等,功能不同。
下面看看如何添加一个普通密码到keychain中
- (void)addIdentityIntoKeyChain { OSStatus sanityCheck = noErr; NSDictionary *dic = [NSDictionarydictionaryWithObjectsAndKeys: (__bridge id)(kSecClassGenericPassword) ,kSecClass, @"this a my description",kSecAttrDescription, [@"1234"dataUsingEncoding:NSUTF8StringEncoding], kSecValueData, // [@"5678" dataUsingEncoding:NSUTF8StringEncoding], kSecValueRef, nil]; NSLog(@"add dic is %@",dic); CFDictionaryRef dicRef = ( __bridgeCFDictionaryRef)dic; sanityCheck = SecItemAdd(dicRef, nil); NSLog(@"%ld",sanityCheck); }
再看看针对上面这个item的2种不同的查询,
第一种,查询具体的密码,即我们真正需要保存的东西
- (void)getPass2 { NSLog(@"pass 2==========="); NSDictionary *queryDictionary = [NSDictionarydictionaryWithObjectsAndKeys: @"this a my description",kSecAttrDescription, kCFBooleanTrue, kSecReturnData, kSecClassGenericPassword, kSecClass, nil]; CFTypeRef handle = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &handle); NSLog(@"status is %ld",status); if (status == noErr) { NSData *data = (__bridge NSData *)handle; NSString *str = [[NSStringalloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"str is %@",str); } }
第二种,查询这个item的各种属性,不查询具体的密码
- (NSString *)getPasswordFromKeyChain { NSLog(@"pass 1==========="); NSDictionary *queryDictionary = [NSDictionarydictionaryWithObjectsAndKeys: @"this a my description",kSecAttrDescription, kCFBooleanTrue, kSecReturnAttributes, kSecClassGenericPassword, kSecClass, nil]; CFTypeRef handle = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &handle); NSLog(@"status is %ld",status); if (status == noErr) { NSDictionary *dic = (__bridge_transfer NSDictionary*) handle ; NSLog(@"dic is %@",dic); } return@""; }
请查看具体的输出,来理解这2中查询的不同。
2014-03-14 10:50:15.338 KeyChainTest[25074:907] add dic is { class = genp; desc = "this a my description"; "v_Data" = <31323334>; } 2014-03-14 10:50:15.356 KeyChainTest[25074:907] -25299 2014-03-14 10:50:15.359 KeyChainTest[25074:907] pass 1=========== 2014-03-14 10:50:15.369 KeyChainTest[25074:907] status is 0 2014-03-14 10:50:15.376 KeyChainTest[25074:907] dic is { acct = ""; agrp = "BDN8QNY54S.com.Kings.test.KeyChainTest"; cdat = "2013-10-16 06:02:54 +0000"; desc = "this a my description"; mdat = "2013-10-16 06:02:54 +0000"; pdmn = ak; svce = ""; } 2014-03-14 10:50:15.381 KeyChainTest[25074:907] pass 2=========== 2014-03-14 10:50:15.389 KeyChainTest[25074:907] status is 0 2014-03-14 10:50:15.393 KeyChainTest[25074:907] str is 1234
另外,在keychain的使用中,还经常涉及一个问题,就是2个程序共享机密信息。这常常发生在以下情况:大型公司专门写了一个用户认证程序,指定的机器可以装上这个程序,用这个程序时要求输入帐号和密码,验证通过后,程序会从服务器取公司的证书和用户的私有密匙,并将他们加入到程序的keychain中 。之后该公司写的其他程序,如果需要帐号和密码,或者证书进行服务器验身份证时,就可以用刚才提到的用户认证程序在keychain中保存的信息。
ios程序默认的keychain是不能够和其他程序共享的,如果想共享自己的信息,那么需要加入到一个Keychain Access Groups中,xcode5在工程设定中可以添加。主要注意的是这个Keychain Access Groups的名字是有限制的,必须和你的provisioning profile向一致,这就保证不可能访问到其他公司的机密信息。
转一些别人的心得:
1.相同bundle下生成的程序都可以共享相同group的keyChain.(我对这一条的理解不太一样,我感觉是这样的:bundle Name 分别是 com.companyName.A 和 com.companyName.B,那么这两个程序的keychain group name 就必须是 com.companyName.xxxx)
相同bundle解释下就是:比如:2个程序分别使用的provision对应bundle是com.jv.key1和com.jv.key2,那你配置文件肯定是{Identifer}.com.jv.{name},其中identifer是苹果生成的随机串号,可以在申请证书时看到,复制过来即可,name可以自己取,程序中指定属于哪个Group即可。
2.如果你在 addkey时,没有指定group,则会默认添加你keychain-access-groups里第一个group(这是keychain group中经常写2个group的原因,简历一个自己的group,防止污染主要的keychain group),如果你没有设置Entitlements,则默认使用对应的程序的bundle name,比如com.jv.key1,表示只能给自己程序使用。
3.如果你程序添加的group并不存在你的配置文件中,程序会奔溃,表示无法添加。因此你只能添加你配置文件中支持的keychain。