Keychain 介紹
Keychain Services 是 OS X 和 iOS 都提供一種安全地存儲(chǔ)敏感信息的工具,比如,存儲(chǔ)用戶ID,密碼,和證書等。存儲(chǔ)這些信息可以免除用戶重復(fù)輸入用戶名和密碼的過程。Keychain Services 的安全機(jī)制保證了存儲(chǔ)這些敏感信息不會(huì)被竊取。簡(jiǎn)單說來,Keychain 就是一個(gè)安全容器。
Keychain 的結(jié)構(gòu)
Keychain 可以包含任意數(shù)量的 keychain item。每一個(gè) keychain item 包含數(shù)據(jù)和一組屬性。對(duì)于一個(gè)需要保護(hù)的 keychain item,比如密碼或者私鑰(用于加密或者解密的string字節(jié))數(shù)據(jù)是加密的,會(huì)被 keychain 保護(hù)起來的;對(duì)于無需保護(hù)的 keychain item,例如,證書,數(shù)據(jù)未被加密。
跟keychain item有關(guān)系的取決于item的類型;應(yīng)用程序中最常用的是網(wǎng)絡(luò)密碼(Internet passwrods)和普通的密碼。正如你所想的,網(wǎng)絡(luò)密碼像安全域(security domain)、協(xié)議、和路徑等一些屬性。在OSX中,當(dāng)keychain被鎖的時(shí)候加密的item沒辦法訪問,如果你想要該問被鎖的item,就會(huì)彈出一個(gè)對(duì)話框,需要你輸入對(duì)應(yīng)keychain的密碼。當(dāng)然,未有密碼的keychain你可以隨時(shí)訪問。但在iOS中,你只可以訪問你自已的keychain items;
item可以指定為以下的類型:
externCFTypeRefkSecClassGenericPasswordexternCFTypeRefkSecClassInternetPasswordexternCFTypeRefkSecClassCertificateexternCFTypeRefkSecClassKeyexternCFTypeRefkSecClassIdentity OSX_AVAILABLE_STARTING(MAC_10_7, __IPHONE_2_0);
Keychain的特點(diǎn)
數(shù)據(jù)并不存放在App的Sanbox中,即使刪除了App,資料依然保存在keychain中。如果重新安裝了app,還可以從keychain獲取數(shù)據(jù)。
keychain的數(shù)據(jù)可以用過group方式,讓程序可以在App間共享。不過得要相同TeamID
keychain的數(shù)據(jù)是經(jīng)過加密的
Keychain的使用
大多數(shù)iOS應(yīng)用需要用到Keychain, 都用來添加一個(gè)密碼,修改一個(gè)已存在Keychain item或者取回密碼。Keychain提供了以下的操作
SecItemAdd 添加一個(gè)item
SecItemUpdate 更新已存在的item
SecItemCopyMatching? 搜索一個(gè)已存在的item
SecItemDelete 刪除一個(gè)keychain item
Talk is Cheap,Show you the Code
根據(jù)特定的Service創(chuàng)建一個(gè)用于操作KeyChain的Dictionary
+ (NSMutableDictionary*)keyChainQueryDictionaryWithService:(NSString*)service{NSMutableDictionary*keyChainQueryDictaionary = [[NSMutableDictionaryalloc]init];? ? [keyChainQueryDictaionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];? ? [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrService];? ? [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrAccount];returnkeyChainQueryDictaionary;}
添加數(shù)據(jù)
+ (BOOL)addData:(id)data forService:(NSString*)service{NSMutableDictionary*keychainQuery = [selfkeyChainQueryDictionaryWithService:service];? ? SecItemDelete((CFDictionaryRef)keychainQuery);? ? [keychainQuery setObject:[NSKeyedArchiverarchivedDataWithRootObject:data] forKey:(id)kSecValueData];? ? OSStatus status= SecItemAdd((CFDictionaryRef)keychainQuery,NULL);if(status == noErr) {returnYES;? ? }returnNO;}
搜索數(shù)據(jù)
+ (id)queryDataWithService:(NSString*)service {idresult;NSMutableDictionary*keyChainQuery = [selfkeyChainQueryDictionaryWithService:service];? ? [keyChainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];? ? [keyChainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];CFDataRefkeyData =NULL;if(SecItemCopyMatching((CFDictionaryRef)keyChainQuery, (CFTypeRef*)&keyData) == noErr) {@try{? ? ? ? ? ? result = [NSKeyedUnarchiverunarchiveObjectWithData:(__bridgeNSData*)keyData];? ? ? ? }@catch(NSException*exception) {NSLog(@"不存在數(shù)據(jù)");? ? ? ? }@finally{? ? ? ? ? ? ? ? ? ? }? ? }if(keyData) {CFRelease(keyData);? ? }returnresult;}
更新數(shù)據(jù)
+ (BOOL)updateData:(id)data forService:(NSString*)service{NSMutableDictionary*searchDictionary = [selfkeyChainQueryDictionaryWithService:service];if(!searchDictionary) {returnNO;? ? }NSMutableDictionary*updateDictionary = [[NSMutableDictionaryalloc] init];? ? [updateDictionary setObject:[NSKeyedArchiverarchivedDataWithRootObject:data] forKey:(id)kSecValueData];? ? OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (CFDictionaryRef)updateDictionary);if(status == errSecSuccess) {returnYES;? ? }returnNO;}
刪除數(shù)據(jù)
+ (BOOL)deleteDataWithService:(NSString*)service{NSMutableDictionary*keyChainDictionary = [selfkeyChainQueryDictionaryWithService:service];? ? OSStatus status = SecItemDelete((CFDictionaryRef)keyChainDictionary);if(status == noErr) {returnYES;? ? }returnNO;}
Keychain 共享數(shù)據(jù)
先開啟Keychain share,選中項(xiàng)目的Target -> Capabilities -> Keychain Groups。打開這個(gè)選項(xiàng)。

open_keychain.png
同時(shí)在你的項(xiàng)目會(huì)生成一個(gè)entitlements文件。里面會(huì)有Access group,值應(yīng)該是
$(AppIdentifierPrefix)cn.xxx.KeyChainLearn
AppIdentifierPrefix表示發(fā)布者的一個(gè)身份,這個(gè)可以在蘋果開發(fā)者后臺(tái)可以找得到。
可以從在Info.plist文件中新增一組key-value
Key: AppIdentifierPrefix
Value: $(AppIdentifierPrefix)
最后創(chuàng)建Keychain Item的時(shí)候,需要指定的相應(yīng)的group
NSString*perfix = [[[NSBundlemainBundle]infoDictionary]objectForKey:@"AppIdentifierPrefix"];NSString*groupString = [NSStringstringWithFormat:@"%@cn.xiaozhi.KeyChainLearn",perfix];[keyChainQueryDictaionary setObject:groupString forKey:(id)kSecAttrAccessGroup];
在其他需要共享數(shù)據(jù)的應(yīng)用也是重復(fù)以上的操作,不過得保證AppIdentifierPrefix相同。
總結(jié)
在保存一些重要的信息的時(shí)候,我們可以使用Keychain,但不是絕對(duì)安全的,可以把數(shù)據(jù)加密后再放在Keychain。Keychain還可以通過iCloud備份跟跨設(shè)備共享。