iOS密碼管理Keychain的使用

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

![keychain(鑰匙串)存儲在iOS系統(tǒng)中,并且恢復iPhone會使keychain的內容也恢復.但是刪除App是不會影響keychain.
不同App之間Keychain是不能相互訪問的,但是可以通過配置keychain-sharing來解決。

image

如圖,在兩個項目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達到共享數(shù)據

如何使用

keychain是一個結構,有很多key-value

image
image
image

此外還有kSecReturnData(返回結果) kSecMatchLimit(查詢策略)等 這兩項是查詢時必選的

1.首選要構建一個存取條件,這些條件指定了要存取的這條內容的存取類型,唯一性,存取策略和內容

一共需要五個key

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

需要注意的是,賬戶名需要預防重名,因為keychain是系統(tǒng)級的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務名稱就好像給宏取名字,表示存儲的這個東西是做什么的.
通過這兩個key就可以指定唯一性
kSecClassGenericPassword表示服務策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲的內容 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{
//構建一個存取條件,實質是一個字典
    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務的名字 可以與服務賬戶名相同
    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{
    // 獲取存儲的數(shù)據的條件
    NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除舊的數(shù)據
    SecItemDelete((CFDictionaryRef)saveQueryDic);
    // 設置新的數(shù)據
    [saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
    // 添加數(shù)據
    OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
    // 釋放對象
    saveQueryDic = nil ;
    // 判斷是否存儲成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過標記獲取數(shù)據查詢條件
    NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 查詢結果返回到 kSecValueData (此項必選)
    [readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    // 只返回搜索到的第一條數(shù)據 (此項必選)
    [readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    // 創(chuàng)建一個對象接受結果
    CFDataRef keyChainData = nil ;
    // 通過條件查詢數(shù)據
    if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
        @try {
            //轉換類型
            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;
    // 返回數(shù)據
    return idObject ;
}

- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 通過標記獲取數(shù)據更新的條件
    NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 創(chuàng)建更新數(shù)據字典
    NSMutableDictionary * newDic = @{}.mutableCopy;
    // 存儲數(shù)據
    [newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    // 獲取存儲的狀態(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ù)據的查詢條件
    NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除指定條件的數(shù)據
    SecItemDelete((CFDictionaryRef)deleteQueryDic);
    deleteQueryDic = nil ;
}

作者:Trigger_o
鏈接:http://www.itdecent.cn/p/6c2265a82f72
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
](//upload-images.jianshu.io/upload_images/3238808-51b18f2d21779dd5.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200)

如圖,在兩個項目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達到共享數(shù)據

如何使用

keychain是一個結構,有很多key-value

image
image
image

此外還有kSecReturnData(返回結果) kSecMatchLimit(查詢策略)等 這兩項是查詢時必選的

1.首選要構建一個存取條件,這些條件指定了要存取的這條內容的存取類型,唯一性,存取策略和內容

一共需要五個key

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

需要注意的是,賬戶名需要預防重名,因為keychain是系統(tǒng)級的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務名稱就好像給宏取名字,表示存儲的這個東西是做什么的.
通過這兩個key就可以指定唯一性
kSecClassGenericPassword表示服務策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲的內容 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{
//構建一個存取條件,實質是一個字典
    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務的名字 可以與服務賬戶名相同
    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{
    // 獲取存儲的數(shù)據的條件
    NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除舊的數(shù)據
    SecItemDelete((CFDictionaryRef)saveQueryDic);
    // 設置新的數(shù)據
    [saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
    // 添加數(shù)據
    OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
    // 釋放對象
    saveQueryDic = nil ;
    // 判斷是否存儲成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過標記獲取數(shù)據查詢條件
    NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 查詢結果返回到 kSecValueData (此項必選)
    [readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    // 只返回搜索到的第一條數(shù)據 (此項必選)
    [readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    // 創(chuàng)建一個對象接受結果
    CFDataRef keyChainData = nil ;
    // 通過條件查詢數(shù)據
    if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
        @try {
            //轉換類型
            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;
    // 返回數(shù)據
    return idObject ;
}

- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 通過標記獲取數(shù)據更新的條件
    NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 創(chuàng)建更新數(shù)據字典
    NSMutableDictionary * newDic = @{}.mutableCopy;
    // 存儲數(shù)據
    [newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    // 獲取存儲的狀態(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ù)據的查詢條件
    NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除指定條件的數(shù)據
    SecItemDelete((CFDictionaryRef)deleteQueryDic);
    deleteQueryDic = nil ;
}

注:文章出處

作者:Trigger_o
鏈接:http://www.itdecent.cn/p/6c2265a82f7

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容