iOS密碼管理Keychain的使用

keychain(鑰匙串)存儲(chǔ)在iOS系統(tǒng)中,并且恢復(fù)iPhone會(huì)使keychain的內(nèi)容也恢復(fù).但是刪除App是不會(huì)影響keychain.
不同App之間Keychain是不能相互訪問(wèn)的,但是可以通過(guò)配置keychain-sharing來(lái)解決。


配置keychain-sharing

如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問(wèn)互相的keychain,以此達(dá)到共享數(shù)據(jù)

如何使用

keychain是一個(gè)結(jié)構(gòu),有很多key-value

指定服務(wù)類型

指定普通密碼存儲(chǔ)的子策略

設(shè)置信息保護(hù)程度

此外還有kSecReturnData(返回結(jié)果) kSecMatchLimit(查詢策略)等 這兩項(xiàng)是查詢時(shí)必選的

1.首選要構(gòu)建一個(gè)存取條件,這些條件指定了要存取的這條內(nèi)容的存取類型,唯一性,存取策略和內(nèi)容

一共需要五個(gè)key

    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問(wèn)
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    //指定存儲(chǔ)的內(nèi)容
    NSString *valueDataKey = (__bridge NSString *)kSecValueData;
    NSString *value = @"要存的內(nèi)容";
    NSDictionary *keychainItems = @{classKey   : classValue,
                                                            accessibleKey : accessibleValue,
                                                            accountKey    : accountValue,
                                                            serviceKey    : serviceValue,
                                                            valueDataKey  : value };

需要注意的是,賬戶名需要預(yù)防重名,因?yàn)閗eychain是系統(tǒng)級(jí)的,配置了keychain-sharing的其他App也可以訪問(wèn),一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過(guò)這兩個(gè)key就可以指定唯一性
kSecClassGenericPassword表示服務(wù)策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲(chǔ)的內(nèi)容 key是kSecValueData

2.增刪改查

- (void)viewDidLoad {
    [super viewDidLoad];

    BOOL succeed = [self keychainSaveData:@"123456789" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    BOOL succeed1 = [self keychainSaveData:@"987654321" withAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
    NSLog(@"%d",succeed);
    NSLog(@"%d",succeed1);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainUpdataData:@"000000000" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainDeleteWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
     NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
}
- (NSMutableDictionary *)keychainDicWithAccountId:(NSString *)accountId andServiceId:(NSString *)serviceId{
//構(gòu)建一個(gè)存取條件,實(shí)質(zhì)是一個(gè)字典
    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問(wèn)
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    NSDictionary *keychainItems = @{classKey      : classValue,
                                    accessibleKey : accessibleValue,
                                    accountKey    : accountValue,
                                    serviceKey    : serviceValue};
    return keychainItems.mutableCopy;
}

- (BOOL)keychainSaveData:(id)aData withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取存儲(chǔ)的數(shù)據(jù)的條件
    NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除舊的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)saveQueryDic);
    // 設(shè)置新的數(shù)據(jù)
    [saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
    // 添加數(shù)據(jù)
    OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
    // 釋放對(duì)象
    saveQueryDic = nil ;
    // 判斷是否存儲(chǔ)成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過(guò)標(biāo)記獲取數(shù)據(jù)查詢條件
    NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 查詢結(jié)果返回到 kSecValueData (此項(xiàng)必選)
    [readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    // 只返回搜索到的第一條數(shù)據(jù) (此項(xiàng)必選)
    [readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    // 創(chuàng)建一個(gè)對(duì)象接受結(jié)果
    CFDataRef keyChainData = nil ;
    // 通過(guò)條件查詢數(shù)據(jù)
    if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
        @try {
            //轉(zhuǎn)換類型
            idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
        } @catch (NSException * exception){
            NSLog(@"Unarchive of search data where %@ failed of %@ ",serviceId,exception);
        }
    }
    if (keyChainData) {
        CFRelease(keyChainData);
    }
    readQueryDic = nil;
    // 返回?cái)?shù)據(jù)
    return idObject ;
}

- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 通過(guò)標(biāo)記獲取數(shù)據(jù)更新的條件
    NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 創(chuàng)建更新數(shù)據(jù)字典
    NSMutableDictionary * newDic = @{}.mutableCopy;
    // 存儲(chǔ)數(shù)據(jù)
    [newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    // 獲取存儲(chǔ)的狀態(tài)
    OSStatus  updataStatus = SecItemUpdate((CFDictionaryRef)updataQueryDic, (CFDictionaryRef)newDic);
    updataQueryDic = nil;
    newDic = nil;
    // 判斷是否更新成功
    if (updataStatus == errSecSuccess) {
        return  YES ;
    }
    return NO;
}


- (void)keychainDeleteWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取刪除數(shù)據(jù)的查詢條件
    NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除指定條件的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)deleteQueryDic);
    deleteQueryDic = nil ;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容