標籤:sdi bridge bubuko enc 修改 imp att 增刪改查 取資料
最近寫新項目,想搞一點高大上的,用keyChain來儲存使用者資訊
keyChain的好處是可以使用蘋果的加密來保證資訊的安全,而且可以在app被刪除之後保留資訊,有傳言在iOS10中keychain中的資料會隨app一起刪除,但是我用iOS11測試的結果是依然保留資料的
keyChain的另一個特性是同一個開發人員帳號下的應用可以共用資料,這個我目前用不到
keyChain雖然有很多優點,但是讀寫資訊還是挺麻煩的,需要兩個string的認證,這相對於plist檔案中的key-value相對來說麻煩一點
而且現在的應用一般不保留使用者密碼,而是用token來驗證使用者身份,所以目前keychain對我來說沒有什麼用
但是我還是要寫一下keychain的使用方法哎嘿嘿
keychain是用SQLite進行儲存的。用蘋果的話來說是一個專業的資料庫,加密我們儲存的資料,可以通過metadata(attributes)進行高效的搜尋。keychain適合儲存一些比較小的資料量的資料,如果要儲存大的資料,可以考慮檔案的形式儲存在磁碟上,在keychain裡面儲存解密這個檔案的密鑰。
keychain的類型
- kSecClassGenericPassword
- kSecClassInternetPassword
- kSecClassCertificate
- kSecClassKey
- kSecClassIdentity
不同類型對應的屬性:
既然蘋果是採用SQLite去儲存的,那麼以上這些不同item的attribute可以理解是資料庫裡面表的欄位。那麼對keychain的操作其實也就是普通資料庫的增刪改查了。這樣也許就會覺得那些API也沒那麼難用了。
下面是我寫的keychainmanager類
#import <Foundation/Foundation.h>@interface KeyChainManager : NSObject+(void)addInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service;+(void)deleteInfoWithAccount:(NSString *)account service:(NSString *)service;+(void)changeInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service;+(NSString *)getInfoWithAccount:(NSString *)account service:(NSString *)service;@end
#import "KeyChainManager.h"@implementation KeyChainManager
/*
向keychain中添加item
info是需要儲存的資訊
account,service是確認item的標識符,keychain通過這兩個值來確定一個item,進行增刪改查
*/
+(void)addInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service{ if (!info) { info = @""; } if (!account || [account isEqualToString:@""]) { NSLog(@"向keychain中添加item失敗原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"向keychain中添加item失敗原因是kSecAttrService不存在"); return; } NSDictionary *query = @{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding], (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) { NSLog(@"向keychain中添加item成功"); }else{ NSLog(@"向keychain中添加item失敗%d",status); }; }//刪除keychain中item+(void)deleteInfoWithAccount:(NSString *)account service:(NSString *)service{ if (!account || [account isEqualToString:@""]) { NSLog(@"刪除keychain中item失敗原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"刪除keychain中item失敗原因是kSecAttrService不存在"); return; } NSDictionary *query = @{ (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) { NSLog(@"刪除keychain中item成功"); }else{ NSLog(@"刪除keychain中item失敗%d",status); }; }//修改keychain中item資料+(void)changeInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service{ if (!info) { info = @""; } if (!account || [account isEqualToString:@""]) { NSLog(@"修改keychain中item資料失敗原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"修改keychain中item資料失敗原因是kSecAttrService不存在"); return; } NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; NSDictionary *update = @{ (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding], }; OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); if (status == errSecSuccess) { NSLog(@"修改keychain中item資料成功"); }else{ NSLog(@"修改keychain中item資料失敗%d",status); }; }//擷取keychain中item資料+(NSString *)getInfoWithAccount:(NSString *)account service:(NSString *)service{ if (!account || [account isEqualToString:@""]) { NSLog(@"擷取keychain中item資料失敗原因是kSecAttrAccount不存在"); return nil; } if (!service || [service isEqualToString:@""]) { NSLog(@"擷取keychain中item資料失敗原因是kSecAttrService不存在"); return nil; } NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecReturnData : @YES, (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; CFTypeRef dataTypeRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); if (status == errSecSuccess) { NSString *pwd = [[NSString alloc] initWithData:(__bridge NSData * _Nonnull)(dataTypeRef) encoding:NSUTF8StringEncoding]; NSLog(@"擷取keychain中item資料成功==result:%@", pwd); return pwd; }else{ NSLog(@"擷取keychain中item資料失敗%d",status); return nil; }; }@end
keychain可以進行應用間的資料共用
同一個開發人員帳號下的應用可以共用存在keychain中的資料
這裡需要到capabilities>keychain sharing
開啟keychain sharing
可以看到有一個group,添加你想要擷取資料的應用A的identifer,就可以擷取它在keychain中的資料
可以對應用A的資料進行增刪改查,但是這對應用A來說不是很安全
iOS - keychain 詳解及變化
https://www.cnblogs.com/junhuawang/p/8194484.html
聊聊iOS Keychain
https://www.cnblogs.com/xiongwj0910/p/7151258.html
iOS -- keyChain