一、简介
keychain 是一个相对独立的空间,生存到 keychain钥匙串中的信息不会由于卸载/重装 app 而丢失, 。相对于 NSUserDefaults、plist 文件生存等一样寻常方式,keychain 生存更为安全。以是我们会用 keyChain 生存一些私密信息,好比暗码、证书、装备唯一码(把获取到用户装备的唯一I D 存到 keychain 内里如许卸载或重装之后还可以获取到 id,包管了一个装备一个ID)等等。keychain 是用 SQLite 举行存储的。用苹果的话来说是一个专业的数据库,加密我们生存的数据,可以通过 metadata(attributes) 举行高效的搜刮。keychain 得当生存一些比力小的数据量的数据,如果要生存大的数据,可以考虑文件的情势存储在磁盘上,在keychain内里生存解密这个文件的密钥。
二、使用
keychain 的使用雷同于数据库,以是也有相应的增编削查操纵的语句。
必要导入 Security 库引入头文件 #import <Security/Security.h>
#import "ViewController.h"#import <Security/Security.h>@interface ViewController ()@end@implementation ViewControllerNSString *const accessItem = @"XXXXXXX.com.miongpao.KeyChainDemo";- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor];}- (NSMutableDictionary *)getKeychainQueryNSString *)service { return [NSMutableDictionary dictionaryWithObjectsAndKeysid)kSecClassGenericPassword,(id)kSecClass,service, (id)kSecAttrService,service, (id)kSecAttrAccount,(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,nil];}/** 增加 */- (void)addKeychainDataid)data forKeyNSString *)key{ NSMutableDictionary *keychainQuery = [self getKeychainQuery:key]; SecItemDelete((__bridge CFDictionaryRef)keychainQuery); [keychainQuery setObject:accessItem forKeyid)kSecAttrAccessGroup]; [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey__bridge id)kSecValueData]; SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);}/** 删除 */- (void)deleteWithServiceNSString *)service { NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; SecItemDelete((CFDictionaryRef)keychainQuery);}/** 修改 */-(void)updateKeychainDataid)data forKeyNSString *)key { NSMutableDictionary *keychainQuery = [self getKeychainQuery:key]; [keychainQuery setObject:accessItem forKeyid)kSecAttrAccessGroup]; NSData * updata = [NSKeyedArchiver archivedDataWithRootObject:data]; NSDictionary *myDate = @{(__bridge id)kSecValueData : updata}; SecItemUpdate((__bridge CFDictionaryRef)keychainQuery, (__bridge CFDictionaryRef)myDate);}/** 查询 */- (id)readForkey:(NSString *)key { id ret = nil; NSMutableDictionary *keychainQuery = [self getKeychainQuery:key]; [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; CFDataRef keyData = NULL; if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { @try { ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; } @catch (NSException *e) { NSLog(@"Unarchive of %@ failed: %@", key, e); } @finally { } } if (keyData)CFRelease(keyData); return ret;}@endkeychain 是一个布局,有很多key-value:
1.key - kSecClass
value阐明kSecClassGenericPassword一样寻常暗码kSecClassInternetPassword网络暗码kSecClassCertificate证书kSecClassKey密钥kSecClassIdentity身份证书(带私钥的证书)2. kSecClassGenericPassword包含的 key
key阐明范例kSecAttrCreationDate创建日期CFDateRefkSecAttrModificationDate末了一次修改日期CFDateRefkSecAttrDescription形貌CFStringRefkSecAttrComment解释CFStringRefkSecAttrCreator创建者CFNumberRef(4字符,如'aLXY')kSecAttrType范例CFNumberRef(4字符,如'aTyp')kSecAttrLabel标签(给用户看)CFStringRefkSecAttrIsInvisible是否隐蔽CFBooleanRef(kCFBooleanTrue,kCFBooleanFalse)kSecAttrIsNegative是否具有暗码,此项表示当前的 item 是否只是一个占位项,大概说是只有 key 没有 valueCFBooleanRef(kCFBooleanTrue,kCFBooleanFalse)kSecAttrAccount账户名CFStringRefkSecAttrService所具有服务CFStringRefkSecAttrGeneric用户自界说内容CFDataRefkSecAttrSecurityDomain网络安全域CFStringRefkSecAttrServer服务器域名或IP地点CFStringRefkSecAttrAccessible可访问性范例透明.3.key - kSecAttrAccessible
这个属性,决定了我们 item 在什么条件下可以获取到内里的内容,我们在添加 item 的时间,可以添加这个属性,来加强数据的安全性,具体的重要有以下几个:
value阐明kSecAttrAccessibleWhenUnlocked解锁可访问,备份kSecAttrAccessibleAfterFirstUnlock第一次解锁后可访问,备份kSecAttrAccessibleAlways不停可访问,备份kSecAttrAccessibleWhenUnlockedThisDeviceOnly解锁可访问,不备份kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly第一次解锁后可访问,不备份kSecAttrAccessibleAlwaysThisDeviceOnly不停可访问,不备份每个意思都很明白,item 默认就是 kSecAttrAccessibleWhenUnlocked,也就是在装备未锁屏的环境下。这个也是苹果保举的。kSecAttrAccessibleAlways 这个苹果在 WWDC 中也说了,不发起使用,苹果本身已经都弃用了。kSecAttrAccessibleAfterFirstUnlock 这个是在装备第一次解锁后,可以使用。这个最常见的就是配景唤醒功能内里,如果必要访问某个 item,那么必要使用这个属性,否则是访问不了 item 的数据的。末了几个 DeviceOnly 干系的设置,如果设置了,那么在手机备份规复到其他装备时,是不能被规复的。同样 iCloud 也不会同步到其他装备,由于在其他装备上是解密不出来的。
三、数据共享
同一个开辟者账号下(teamID),各个应用之间可以共享 item。keychain 通过 keychain-access-groups
来举行访问权限的控制。在 Xcode 的 Capabilities 选项中打开 Keychain Sharing 即可。
//添加 NSDictionary *query = @{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecValueData : [@"1234562" dataUsingEncoding:NSUTF8StringEncoding], (__bridge id)kSecAttrAccount : @"account name", (__bridge id)kSecAttrAccessGroup : @"XEGH3759AB.com.developer.test", (__bridge id)kSecAttrService : @"noraml1", (__bridge id)kSecAttrSynchronizable : @YES, }; CFErrorRef error = NULL; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); //读取 NSDictionary *query1 = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecReturnRef : @YES, (__bridge id)kSecReturnData : @YES, (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll, (__bridge id)kSecAttrAccount : @"account name", (__bridge id)kSecAttrAccessGroup : @"XEGH3759AB.com.developer.test", (__bridge id)kSecAttrService : @"noraml1", }; CFTypeRef dataTypeRef = NULL; OSStatus status1 = SecItemCopyMatching((__bridge CFDictionaryRef)query1, &dataTypeRef) //只必要添加一个kSecAttrAccessGroup属性即可。 |