iOS密碼管理Keychain的使用

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


配置keychain-sharing

如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問互相的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í)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對應(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也可以訪問,一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過這兩個(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í)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對應(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);
    // 釋放對象
    saveQueryDic = nil ;
    // 判斷是否存儲(chǔ)成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過標(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è)對象接受結(jié)果
    CFDataRef keyChainData = nil ;
    // 通過條件查詢數(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{
    // 通過標(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)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子每界,更是在濱河造成了極大的恐慌恼策,老刑警劉巖劣欢,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杉畜,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹭秋,警方通過查閱死者的電腦和手機(jī)扰付,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仁讨,“玉大人羽莺,你說我怎么就攤上這事《椿恚” “怎么了盐固?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丈挟。 經(jīng)常有香客問我刁卜,道長,這世上最難降的妖魔是什么曙咽? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任蛔趴,我火速辦了婚禮,結(jié)果婚禮上例朱,老公的妹妹穿的比我還像新娘夺脾。我一直安慰自己之拨,他們只是感情好茉继,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布咧叭。 她就那樣靜靜地躺著,像睡著了一般烁竭。 火紅的嫁衣襯著肌膚如雪菲茬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天派撕,我揣著相機(jī)與錄音婉弹,去河邊找鬼。 笑死终吼,一個(gè)胖子當(dāng)著我的面吹牛镀赌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播际跪,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼商佛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了姆打?” 一聲冷哼從身側(cè)響起良姆,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幔戏,沒想到半個(gè)月后玛追,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闲延,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年痊剖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垒玲。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陆馁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侍匙,到底是詐尸還是另有隱情氮惯,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布想暗,位于F島的核電站妇汗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏说莫。R本人自食惡果不足惜杨箭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望储狭。 院中可真熱鬧互婿,春花似錦捣郊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驮配,卻和暖如春娘扩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壮锻。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工琐旁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猜绣。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓灰殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掰邢。 傳聞我的和親對象是個(gè)殘疾皇子牺陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355