IOS在鑰匙串裡保存APP的賬號密碼。想在應用裡保存帳號、密碼等信息的話,直接存到 plist 裡顯然是不負責任的,自己折騰加密既麻煩又不能保證不被反編譯破解。實際上蘋果 iOS 和 Mac OS X 系統自帶了一套敏感信息保存方案:"鑰匙串" (Keychain)。
用原生的 Security.framework 就可以實現鑰匙串的訪問、讀寫。但是只能在真機上進行,模擬器會出錯。在 Github 上有個封裝的非常好的類來實現這個功能,讓你既能在模擬器又能在真機上訪問鑰匙串。
具體代碼:
//獲取密碼
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
//保存密碼
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
//刪除密碼
+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
直接使用Security框架讀寫鑰匙串。
我們使用第三方類SFHFKeychainUtils來操作鑰匙串 (GitHub代碼下載)
使用方法如下:
1、引入Security.framework框架。
2、引入頭文件:#import"SFHFKeychainUtils.h"
3、存密碼:
view plain copy
NSString*SERVICE_NAME=@"demo";[SFHFKeychainUtilsstoreUsername:@"dd"
andPassword:@"aa"forServiceName:SERVICE_NAME
updateExisting:1error:nil];
4、取密碼:
view plain copy
NSString*passWord=[SFHFKeychainUtilsgetPasswordForUsername:@"dd"andServiceName:SERVICE_NAME
error:nil];NSLog(@"%@",passWord);
5、刪除用戶:
view plain copy [SFHFKeychainUtilsdeleteItemForUsername:@"dd"andServiceName:SERVICE_NAMEerror:nil];
iOS Keychain鑰匙串,應用間數據共享打造iOS上的全家桶
Demo先行:https://github.com/rayshen/GIKeychainGroupDemo
該demo裡有2個工程,你先運行任何一個會存儲一個值,再運行另一個會訪問之前的app存儲的值,並修改。
官方:https://developer.apple.com/library/ios/samplecode/GenericKeychain/Introduction/Intro.html
之前博客使用過Keychain,實現了數據刪除APP後還能保存,但是並沒有實現APP間的共享。
實現APP間的數據共享,主要依賴於在數據存入鑰匙串時,使用同一個鑰匙串條目。
主要分為兩部分:
1.賦予應用對某個鑰匙串條目的訪問權限。
2.寫入時配置鑰匙串條目,對kSecAttrAccessGroup的值進行設置。
一、APP對鑰匙串的訪問權限:
(1)未對應用APP的entitlement(授權)進行配置時,APP使用鑰匙串存儲時,會默認存儲在自身BundleID的條目下。
(2)對APP的entitlement(授權)進行配置後,說明APP有了對某個條目的訪問權限。
鑰匙串的可視化效果可參見Mac的APP-鑰匙串訪問。
APP鑰匙串訪問權限的配置方法:(這裡XXXXX模擬器隨意,但真機必須為自己開發者賬號ID,否則無法通過編譯)
1.新建一個Plist文件,在Plist中的數組中添加可以訪問的條目的名字(如KeychainAccessGroups.plist),結構如下:
Plist代碼:
keychain-access-groups
XXXXX.GrassInfoAppFamily
2.在Build-setting中進行配置,搜索entitlement,注意路徑別配置錯:
二、APP對鑰匙串的操作:
鑰匙串的操作接口都位於Security.framework框架下,它是一個sqlite數據庫,位於/private/var/Keychains/keychain-2.db,其保存的所有數據都是加密過的。
其過程可以總結為:
1.配置查詢字典,格式是NSMutableDictionary,需要配置的內容下次再分析,功能就相當於寫一句SQL一樣。
2.進行增(SecItemAdd)、刪(SecItemDelete)、改(SecItemUpdate)、查(SecItemCopyMatching)。
代碼Demo裡面有,這裡以增為例,下面有2個語句,一個是增加到自身BundleID的鑰匙串條目,一個是增加到共享的條目中。
//創建一個基本的查詢字典
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
service, (__bridge id)kSecAttrService,
service, (__bridge id)kSecAttrAccount,
(__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
nil];
}
+ (void)addKeychainData:(id)data forKey:(NSString *)key{
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
//Delete old item before add new item
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
+(void)addShareKeyChainData:(id)data forKey:(NSString *)key{
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
[keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup];
//Delete old item before add new item
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
函數 [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup] 的配置,就是指定了這次寫入時的鑰匙串條目,不寫入時默認為plist配置文件裡第一個條目。
在查詢中,也可以對查詢的鑰匙串條目進行配置,默認會對所有有權限的條目進行搜索。