keychain在ios中是保存在sqlite数据库中的。
这个数据库文件的位置:真机:/private/var/Keychains/keychain-2.db虚拟机:/Users/USER-HOME/Library/Developer/CoreSimulator/Devices/26DCA62C-B516-4DEA-A601-5C2D0EA07710/data/Library/Keychains/keychain-2-debug.db在虚拟机中,这个数据库考出来就不能读了,很奇怪。下面的每一项都代表一张表。每张表的字段是不同的。kSecClassGenericPassword 对应的表:genpkSecClassInternetPassword 对应的表:inetkSecClassCertificate 对应的表:certCFTypeRef kSecClassKey 对应的表:keysCFTypeRef kSecClassIdentity 对应的表:没找到,应该是cert和key这两个表联起来用,各放一部分。每个表拥有的字段都是以 kSecAttr开头定义的。表中有些字段是系统自己维护的,如cdate:创建时间,mdate:修改时间还有创建者等。这些字段都指定了数据类型,所以什么字段放什么类型的值都是字段定死的。比如,kSecClassGenericPassword下的kSecAttrService就只能保存字符串。因为有些字段的值是枚举类型的,比如kSecClassInternetPassword下的kSecAttrProtocol字段,就是一个枚举类型,所以定义了很多kSecAttrProtocol开头的常量。
最常用的kSecClassGenericPassword表:genp
这个表的主键是kSecAttrAccount 和kSecAttrService ,所以新建一项SecItem时,这两项在已有项中不能重。
这三个常量都对应表的data字段,但在从取出后,会转为不同的数据类型。这个字段是会加密保存的。kSecValueDatakSecValueRefkSecValuePersistentRefkSecClassGenericPassword item attributes: kSecAttrAccessible kSecAttrAccessControl kSecAttrAccessGroup 对应字段:agrp kSecAttrCreationDate 对应字段:cdat kSecAttrModificationDate 对应字段:mdat kSecAttrDescription 对应字段:desc kSecAttrComment kSecAttrCreator 对应字段:crtr kSecAttrType 对应字段:type kSecAttrLabel 对应字段:labl kSecAttrIsInvisible 对应字段:invi kSecAttrIsNegative 对应字段:nega kSecAttrAccount 对应字段:acct kSecAttrService 对应字段:svce kSecAttrGeneric 对应字段:gena
苹果官方的KeychainItemWrapper库中用的就是kSecClassGenericPassword,但是用错了。这个库太老了,而且很久没更新了,不建议用。
- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;把identifier的值设给了kSecAttrGeneric字段,这是错的,因为kSecAttrGeneric不是主键。所以下面的代码中,建第二项时会报错,原因就是主键重复了。 KeychainItemWrapper * keychin1 = [[KeychainItemWrapper alloc]initWithIdentifier:@"pwd1" accessGroup:nil]; [keychin1 setObject:pwd1 forKey:(__bridge id)kSecValueData];//上面的执行后,下面的执行出错: KeychainItemWrapper * keychin2 = [[KeychainItemWrapper alloc]initWithIdentifier:@"pwd1" accessGroup:nil]; [keychin2 setObject:pwd2 forKey:(__bridge id)kSecValueData];换成下面的就可以了: KeychainItemWrapper * keychin2 = [[KeychainItemWrapper alloc]initWithIdentifier:@"pwd2" accessGroup:nil]; [keychin2 setObject:pwd2 forKey:(__bridge id)kSecAttrService];