https://www.cnblogs.com/m4abcd/p/5242254.html
Keychain 使用? ---為了實用最大化我覺得我應該直接先說使用!
當然是使用第三方庫啦:sskeychain 3000+星星的庫不開玩笑。github位址:https://github.com/soffes/sskeychain
導入完之後首先,編譯一下有無錯。
如果是自己手動導入:
1.把SSKeychain.h SSKeychain.m SSKeychainQuery.h SSKeychainQuery.m 複制到工程
2.添加Security.framework 怎麼添加?點一下那個+
3.SSKeychain.h有錯?把SSKeychain.h 中的#import <SSKeychain/SSKeychainQuery.h> 換成 #import <Foundation/Foundation.h> #import "SSKeychainQuery.h" 吧。
還有錯?作為小白我的也不知道了,發我郵件一起讨論吧。
接下來示範4個過程
基本說明:儲存的資料有三個 1.服務名(這個友善對賬号密碼進行分類)2.賬号3.密碼 而這三個資料都是NSString (如果要存其他類型呢,請看後面吧)
所用到的API :
添加和更新都用這個: + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account ;
查詢密碼:+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
删除:+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
1.添加一條鑰匙 (這個鑰匙的資訊 由 服務名+賬号+密碼 組成)
記得添加頭檔案
#import "SSKeychain.h"
#import "SSKeychainQuery.h"
//先定義一下要用的東東
NSString *serviceName= @"com.keychaintest.data";
NSString *account = @"m4abcd";
NSString *password = @"12345678";
//加入鑰匙串!
if ([SSKeychain setPassword:password forService:serviceName account:account]) {
NSLog(@"success !");
}
說明:就是這麼簡單咯。
2.查詢
1.查詢某service 下 count 的密碼并且列印出來:
NSLog(@"%@",[SSKeychain passwordForService:serviceName account:account]);
2.查詢service下所有鑰匙:
NSArray *keys = [SSKeychain accountsForService:serviceName];
這是我的輸出:
2016-03-04 15:08:43.785 keychaintest[31342:4403403] (
{
acct = m4abcd;
agrp = test;
cdat = "2016-03-03 07:10:58 +0000";
mdat = "2016-03-04 07:08:43 +0000";
pdmn = ak;
svce = "com.keychaintest.data";
sync = 0;
tomb = 0;
}
)
說明:傳回的結果為數組,數組成員就是我們查詢的鑰匙,這裡隻有一個鑰匙,而鑰匙資訊以字典的形式建構的,鍵acct 就是count,鍵svce 就是serviceName。密碼在哪裡?用方法1去取吧騷年!
3.查詢本appkeychain的所有鑰匙
NSArray *keys = [SSKeychain allAccounts];
3.更新
if([SSKeychain setPassword:@"321321" forService:serviceName account:account]){
NSLog(@"set success!");
}
4.删除
if([SSKeychain deletePasswordForService:serviceName account:account]){
NSLog(@"delete success!");
}
說明:删除就是把這一條鑰匙删除哦,不是隻删除密碼!
另外的說明:如果你的password 是NSData
查詢: + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
設定or更新:+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
下面開始淺淺的了解還有對蘋果API進行一點點說明吧
1.Keychain 是什麼?
keychain 就是放鑰匙櫃子!就是蘋果提供給我們的一個保險櫃。
這篇文章僅針對iOS。
在iOS中每個APP 都有屬于自己的Keychain,最常用就是儲存使用者的賬戶和密碼,就是記住密碼,放在這裡很安全(蘋果負責幫我們加密再存起來,如果出了問題怪他咯!),假如用NSUserDefault 儲存這些秘密資料,生成的plist檔案(就放在那個Library/Preferences 下)容易被拿到,而且還要自己做加密。
特性:1.當app删除了,又再次重新安裝,這個保險櫃裡的資訊還存在哦。 是以當你的某女同學登了APP并儲存了密碼,你重裝了APP,如果不删除記錄,你女票還是可以發現的。
2.安全!作為小白的我并不知道它實際上是存在哪裡的。
2.Keychain 組成?
1.組成部分由 {N個标簽(屬性) + 一個重要資料} 組成!
2.結構可以看成是一個字典的形式大概是這樣的: @{@"屬性key1":@"屬性值1",@"屬性keyN":@"屬性值N",@"valueData":@資料}
3.内容說明:
一個重要資料:就是密碼password!
N個标簽:也是屬性,都是用來表明這條鑰匙的,如我們的serviceName ,account 都是屬性,他們對應的鍵為 kSecAttrAccount 和 kSecAttrAccount,還有系統給我們加的建立時間,修改時間等還有label,type,port,你自己打開檔案進去看看吧,這些标簽的任務就是來表明這條鑰匙是獨一無二的。
3.原始API操作
先來看看幾個API
添加鑰匙: OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
查詢密碼與查詢标簽: OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINEDresult)
更新鑰匙資訊: OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
删除鑰匙: OSStatus SecItemDelete(CFDictionaryRef query)
先說明一下 這些API的關鍵在于1.是了解和配置好這個操作字典 2.注意傳回的OSStatus 狀态 3.CF對象與OC 之間的bridge
1.先來一發查找
過程:
1.(關鍵)先配置一個操作字典内容有:
kSecAttrService(屬性),kSecAttrAccount(屬性) 這些屬性or标簽是查找的依據
kSecReturnData(值為@YES 表明傳回類型為data),kSecClass(值為kSecClassGenericPassword 表示重要資料為“一般密碼”類型) 這些限制條件是傳回結果類型的依據
2.然後用查找的API 得到查找狀态和傳回資料(密碼)
3.最後如果狀态成功那麼将資料(密碼)轉換成string 傳回
//用原生的API 實作查詢密碼
- (NSString *)passwordForService:(nonnull NSString *)service account:(nonnull NSString *)account{
//生成一個查詢用的 可變字典
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:4];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; //表明為一般密碼可能是證書或者其他東西
[dict setObject:service forKey:(__bridge id)kSecAttrService]; //輸入service
[dict setObject:account forKey:(__bridge id)kSecAttrAccount]; //輸入account
[dict setObject:@YES forKey:(__bridge id)kSecReturnData]; //傳回Data
//查詢
OSStatus status = -1;
CFTypeRef result = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);//核心API 查找是否比對 和傳回密碼!
if (status != errSecSuccess) { //判斷狀态
return nil;
}
//傳回資料
NSString *password = [[NSString alloc] initWithData:(__bridge_transfer NSData *)result encoding:NSUTF8StringEncoding];//轉換成string
return password;
}
說明:其實關鍵就在于這個操作字典的配置上!
2.添加&更新
說明:當添加的時候我們一般需要判斷一下目前鑰匙串裡面是否已經存在我們要添加的鑰匙。如果已經存在我們就更新好了,不存在再添加,是以這兩個操作一般寫成一個函數搞定吧。
過程關鍵:1.檢查是否已經存在 建構的查詢用的操作字典:kSecAttrService,kSecAttrAccount,kSecClass(标明存儲的資料是什麼類型,值為kSecClassGenericPassword 就代表一般的密碼)
2.添加用的操作字典: kSecAttrService,kSecAttrAccount,kSecClass,kSecValueData
3.更新用的操作字典1(用于定位需要更改的鑰匙):kSecAttrService,kSecAttrAccount,kSecClass
操作字典2(新資訊)kSecAttrService,kSecAttrAccount,kSecClass ,kSecValueData
//用原生的API 添加一條鑰匙
-(BOOL)addItemWithService:(NSString *)service account:(NSString *)account password:(NSString *)password{
//先查查是否已經存在
//構造一個操作字典用于查詢
NSMutableDictionary *searchDict = [[NSMutableDictionary alloc]initWithCapacity:4];
[searchDict setObject:service forKey:(__bridge id)kSecAttrService]; //标簽service
[searchDict setObject:account forKey:(__bridge id)kSecAttrAccount]; //标簽account
[searchDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];//表明存儲的是一個密碼
OSStatus status = -1;
CFTypeRef result =NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDict, &result);
if (status == errSecItemNotFound) { //沒有找到則添加
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; //把password 轉換為 NSData
[searchDict setObject:passwordData forKey:(__bridge id)kSecValueData]; //添加密碼
status = SecItemAdd((__bridge CFDictionaryRef)searchDict, NULL); //!!!!!關鍵的添加API
}else if (status == errSecSuccess){ //成功找到,說明鑰匙已經存在則進行更新
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; //把password 轉換為 NSData
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:searchDict];
[dict setObject:passwordData forKey:(__bridge id)kSecValueData]; //添加密碼
status = SecItemUpdate((__bridge CFDictionaryRef)searchDict, (__bridge CFDictionaryRef)dict);//!!!!關鍵的更新API
}
return (status == errSecSuccess);
}