IOS的keychain的三種使用方法
東東 • 2016 年 09 月 11 日
一、keychain介紹
根據蘋果的介紹,iOS裝置中的Keychain是一個安全的存儲容器,可以用來為不同應用儲存敏感資訊比如使用者名,密碼,網絡密碼,認證令牌。蘋果自己用keychain來儲存Wi-Fi網絡密碼,VPN憑證等等。它是一個sqlite資料庫,位于/private/var/Keychains/keychain-2.db,其儲存的所有資料都是加密過的。
開發者通常會希望能夠利用作業系統提供的功能來儲存憑證(credentials)而不是把它們(憑證)儲存到NSUserDefaults,plist檔案等地方。儲存這些資料的原因是開發者不想使用者每次都要登入,是以會把認證資訊儲存到裝置上的某個地方并且在使用者再次打開應用的時候用這些資料自動登入。Keychain的資訊是存在于每個應用(app)的沙盒之外的。
這個資料是存在系統的,是以就算解除安裝軟體,也照樣存在機器中,除非恢複系統
二、keychain的使用
這裡總結keychain三個使用方法,分别是蘋果官方的KeychainItemWrapper
第三方封裝sskeychain
通過Security.framework架構使用
這三個方法我最推崇的是使用sskeychain這個封裝的方案,更加簡單友善,下載下傳和使用位址在後面,現在開始說下每一個方法的使用
三、KeychainItemWrapper的使用
KeychainItemWrapper是蘋果官方推出的,連結位址:點選進入官方文檔,這個因為是官方推出的,是以很多人用,但是會有點坑,使用方案,首先去官方位址或者後面我的demo中,把KeychainItemWrapper.h和KeychainItemWrapper.m引入工程,因為這個是不支援arc的,是以引入之後需要到工程的設定中,把KeychainItemWrapper.m使用-fno-objc-arc這個關閉arc//蘋果官方keychain使用
-(void)AppleKeyChain
{
NSLog(@ "AppleKeyChain ");
//辨別符(Identifier)在後面我們要從keychain中取資料的時候會用到。如果你想要在應用之間共享資訊,那麼你需要指定通路組(access group)。有同樣的通路組 的應用能夠通路同樣的keychain資訊。
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:DAMON accessGroup:nil];
//讀取
if (![wrapper objectForKey:(id)kSecValueData]) {
NSLog(@ "沒有blog這個key ");
}
else{
NSLog(@ "%@ ",[wrapper objectForKey:(id)kSecValueData]);
}
// 設定service 必須
[wrapper setObject:DAMON forKey:(id)kSecAttrService];
//設定account 必須
[wrapper setObject:DAMON forKey:(id)kSecAttrAccount];
[wrapper setObject:@ "hudongdongspp " forKey:(id)kSecValueData];
//設定通路權限
[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];
}
使用需要注意幾點[wrapper setObject: forKey:]中,key必須要是他提供的那幾個,在secitem.h裡面,點選kSecAttrService就可以跳轉進去,不能是自定義的
keychain内部應該是根據kSecAttrService和kSecAttrAccount作為辨別的,是以必須設定,否則會崩潰
如果[[KeychainItemWrapper alloc] initWithIdentifier:DAMON accessGroup:nil];裡面的Identifier改變了,那麼需要把kSecAttrService和kSecAttrAccount也做改變,否則會提示Couldn't add the Keychain Item.或者Couldn't update the Keychain Item.這兩個錯誤
辨別符(Identifier)在後面我們要從keychain中取資料的時候會用到。如果你想要在應用之間共享資訊,那麼你需要指定通路組(access group)。有同樣的通路組的應用能夠通路同樣的keychain資訊。
kSecAttrAccessiblein變量用來指定這個應用合适需要通路這個資料。我們需要對這個選項特别注意,并且使用最嚴格的選項。這個鍵(key)可以設定6種值。當然,我們應該絕對不要使用kSecAttrAccessibleAlways。一個安全點的選項是kSecAttrAccessibleWhenUnlocked。有些選項是以 ThisDeviceOnly 結尾的,如果選中了這個選項,那麼資料就會被以硬體相關的密鑰(key)加密,是以不能被傳輸到或者被其他裝置看到。即使它們提供了進一步的安全性,使用它們可能不是一個好主意,除非你有一個更好的理由不允許資料在備份之間遷移。
四、sskeychain的使用方法
sskeychain是samsoffes大神封裝的一個方法,不像KeychainItemWrapper需要設定太多的選項,是以很好用
使用方法就是下載下傳之後,把SSKeychain.h和SSKeychain.m檔案拖入到自己的工程中,導入頭檔案即可//SSKeychain使用方法
-(void)SSKeychain
{
NSLog(@ "SSKeychain ");
//讀取
if (![SSKeychain passwordForService:@ "blog " account:@ "hu "]) {
NSLog(@ "沒有 ");
}
else{
NSLog(@ "%@ ",[SSKeychain passwordForService:@ "blog " account:@ "hu "]);
}
//寫入
[SSKeychain setPassword:@ "damon " forService:@ "blog " account:@ "hu "];
[SSKeychain setAccessibilityType:kSecAttrAccessibleAlwaysThisDeviceOnly];
}
這個操作起來比較簡單明了,其實就是内部已經封裝好了service等内容,不會出現KeychainItemWrapper這種沒有設定就會崩潰的狀況
五、Security.framework的使用
這個用到了系統庫#import Security/Security.h
是用SecItemDelete((CFDictionaryRef)keychainQuery);
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
這些方法來操作keychain的
但是在網上搜尋,幾乎都是這個方法,是以就測試了下可以通過,是以就記錄下原理+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
這個方法其實就是通過service這一個參數就和keychainitemwrapper裡面提前預設了參數一樣,把kSecAttrAccount和kSecAttrService直接用service一個參數設定了,然後把通路類型也預置了。
是以讀寫都會先調用這個預置的函數,就是獲得同一個service裡面的内容//儲存
+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
//讀
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[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: %@ ", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
//删除
+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
之後使用對應的方法即可,這個函數是在demo裡面的customKeyChainTool檔案裡面
使用方法可以這樣使用//SecurityKeychain使用方法
-(void)SecurityKeychain
{
NSLog(@ "SecurityKeychain ");
NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
[usernamepasswordKVPairs setObject:@ "damon " forKey:@ "key "];
[customKeyChainTool save:@ "mmmmm " data:usernamepasswordKVPairs];
NSLog(@ "%@ ",[(NSMutableDictionary*)[customKeyChainTool load:@ "mmmmm "] objectForKey:@ "key "]);
}
六、demo下載下傳:
七、參考文章版權屬于:胡東東部落格
自2017年12月26日起,『轉載以及大段采集進行後續編輯』須注明本文标題和連結!否則禁止所有轉載和采集行為!
☟☟如文章有用,可點選一次下方廣告支援一下☟☟