iOS Keychain理解

Keychain 介紹

Keychain Services 是 OS X 和 iOS 都提供一種安全地存儲(chǔ)敏感信息的工具,比如,存儲(chǔ)用戶ID,密碼,和證書等。存儲(chǔ)這些信息可以免除用戶重復(fù)輸入用戶名和密碼的過(guò)程。Keychain Services 的安全機(jī)制保證了存儲(chǔ)這些敏感信息不會(huì)被竊取。簡(jiǎn)單說(shuō)來(lái),Keychain 就是一個(gè)安全容器。

Keychain 的結(jié)構(gòu)

Keychain 可以包含任意數(shù)量的 keychain item。每一個(gè) keychain item 包含數(shù)據(jù)和一組屬性。對(duì)于一個(gè)需要保護(hù)的 keychain item,比如密碼或者私鑰(用于加密或者解密的string字節(jié))數(shù)據(jù)是加密的,會(huì)被 keychain 保護(hù)起來(lái)的;對(duì)于無(wú)需保護(hù)的 keychain item,例如,證書,數(shù)據(jù)未被加密。

跟keychain item有關(guān)系的取決于item的類型;應(yīng)用程序中最常用的是網(wǎng)絡(luò)密碼(Internet passwrods)和普通的密碼。正如你所想的,網(wǎng)絡(luò)密碼像安全域(security domain)、協(xié)議、和路徑等一些屬性。在OSX中,當(dāng)keychain被鎖的時(shí)候加密的item沒(méi)辦法訪問(wèn),如果你想要該問(wèn)被鎖的item,就會(huì)彈出一個(gè)對(duì)話框,需要你輸入對(duì)應(yīng)keychain的密碼。當(dāng)然,未有密碼的keychain你可以隨時(shí)訪問(wèn)。但在iOS中,你只可以訪問(wèn)你自已的keychain items;

item可以指定為以下的類型:

extern CFTypeRef kSecClassGenericPassword
extern CFTypeRef kSecClassInternetPassword
extern CFTypeRef kSecClassCertificate
extern CFTypeRef kSecClassKey
extern CFTypeRef kSecClassIdentity OSX_AVAILABLE_STARTING(MAC_10_7, __IPHONE_2_0);

Keychain的特點(diǎn)

  • 數(shù)據(jù)并不存放在App的Sanbox中,即使刪除了App,資料依然保存在keychain中。如果重新安裝了app,還可以從keychain獲取數(shù)據(jù)。
  • keychain的數(shù)據(jù)可以用過(guò)group方式,讓程序可以在App間共享。不過(guò)得要相同TeamID
  • keychain的數(shù)據(jù)是經(jīng)過(guò)加密的

Keychain的使用

大多數(shù)iOS應(yīng)用需要用到Keychain, 都用來(lái)添加一個(gè)密碼,修改一個(gè)已存在Keychain item或者取回密碼。Keychain提供了以下的操作

  • SecItemAdd 添加一個(gè)item
  • SecItemUpdate 更新已存在的item
  • SecItemCopyMatching 搜索一個(gè)已存在的item
  • SecItemDelete 刪除一個(gè)keychain item

Talk is Cheap,Show you the Code

根據(jù)特定的Service創(chuàng)建一個(gè)用于操作KeyChain的Dictionary

+ (NSMutableDictionary *)keyChainQueryDictionaryWithService:(NSString *)service{
    NSMutableDictionary *keyChainQueryDictaionary = [[NSMutableDictionary alloc]init];
    [keyChainQueryDictaionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrService];
    [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrAccount];
    return keyChainQueryDictaionary;
}

添加數(shù)據(jù)

+ (BOOL)addData:(id)data forService:(NSString *)service{
    NSMutableDictionary *keychainQuery = [self keyChainQueryDictionaryWithService:service];
    SecItemDelete((CFDictionaryRef)keychainQuery);
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    OSStatus status= SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
    if (status == noErr) {
        return YES;
    }
    return NO;
}

搜索數(shù)據(jù)

+ (id)queryDataWithService:(NSString *)service {
    id result;
    NSMutableDictionary *keyChainQuery = [self keyChainQueryDictionaryWithService:service];
    [keyChainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [keyChainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)keyChainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            result = [NSKeyedUnarchiver  unarchiveObjectWithData:(__bridge NSData *)keyData];
        }
        @catch (NSException *exception) {
            NSLog(@"不存在數(shù)據(jù)");
        }
        @finally {
            
        }
    }
    if (keyData) {
        CFRelease(keyData);
    }
    return result;
}

更新數(shù)據(jù)

+ (BOOL)updateData:(id)data forService:(NSString *)service{
    NSMutableDictionary *searchDictionary = [self keyChainQueryDictionaryWithService:service];
    
    if (!searchDictionary) {
        return NO;
    }
    
    NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
    [updateDictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
                                    (CFDictionaryRef)updateDictionary);
    if (status == errSecSuccess) {
        return YES;
    }
    return NO;
}

刪除數(shù)據(jù)

+ (BOOL)deleteDataWithService:(NSString *)service{
    NSMutableDictionary *keyChainDictionary = [self keyChainQueryDictionaryWithService:service];
    OSStatus status = SecItemDelete((CFDictionaryRef)keyChainDictionary);
    if (status == noErr) {
        return YES;
    }
    return NO;
}

Keychain 共享數(shù)據(jù)

先開啟Keychain share,選中項(xiàng)目的Target -> Capabilities -> Keychain Groups。打開這個(gè)選項(xiàng)。

open_keychain.png

同時(shí)在你的項(xiàng)目會(huì)生成一個(gè)entitlements文件。里面會(huì)有Access group,值應(yīng)該是

$(AppIdentifierPrefix)cn.xxx.KeyChainLearn

AppIdentifierPrefix表示發(fā)布者的一個(gè)身份,這個(gè)可以在蘋果開發(fā)者后臺(tái)可以找得到。

可以從在Info.plist文件中新增一組key-value

Key: AppIdentifierPrefix
Value: $(AppIdentifierPrefix)

最后創(chuàng)建Keychain Item的時(shí)候,需要指定的相應(yīng)的group

NSString *perfix = [[[NSBundle mainBundle]infoDictionary]objectForKey:@"AppIdentifierPrefix"];
NSString *groupString = [NSString stringWithFormat:@"%@cn.xiaozhi.KeyChainLearn",perfix];
[keyChainQueryDictaionary setObject:groupString forKey:(id)kSecAttrAccessGroup];

在其他需要共享數(shù)據(jù)的應(yīng)用也是重復(fù)以上的操作,不過(guò)得保證AppIdentifierPrefix相同。

總結(jié)

在保存一些重要的信息的時(shí)候,我們可以使用Keychain,但不是絕對(duì)安全的,可以把數(shù)據(jù)加密后再放在Keychain。Keychain還可以通過(guò)iCloud備份跟跨設(shè)備共享。

參考文檔:

官方文檔

iOS筆記: 使用Keychain在App間共享資料

Keychain

最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,148評(píng)論 4 61
  • keychain app官方鏈接 重要的事情說(shuō)三遍 使用keychain group的時(shí)候,測(cè)試一定要使用真機(jī)! ...
    Rxiaobing閱讀 3,348評(píng)論 1 5
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評(píng)論 25 709
  • 在微博看到了一個(gè)博主推了簡(jiǎn)書 一直是一個(gè)喜歡在備忘錄寫寫東西的人 第一次在公開的平臺(tái)寫一些自己感觸. 特別喜歡攝...
    W南墻閱讀 264評(píng)論 2 1
  • 上周,公公婆婆提前來(lái)廣州過(guò)年,我又開始了久違的和公婆一起的生活。 家里請(qǐng)了阿姨,做飯帶孩子都不用他們操心,就想讓公...
    龔導(dǎo)輝閱讀 359評(píng)論 3 8

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