iOS keyChain 研究

http://blog.csdn.net/jerryvon/article/details/16843065

一.基本知識
1.方法
SecItemAdd 增
SecItemUpdate 改
SecItemDelete 刪
SecItemCopyMatching 查

2.權(quán)限
文檔上說iOS的keyChain是一個相對獨(dú)立的空間,當(dāng)程序替換,刪除時并不會刪除keyChain的內(nèi)容,這個要比Library/Cache好。刷機(jī),恢復(fù)出廠應(yīng)該就沒有了。關(guān)于備份,只會備份數(shù)據(jù),到那時不會備份設(shè)備的密鑰,換句話說,即使拿到數(shù)據(jù),也沒有辦法解密里面的內(nèi)容。有人說似乎破解的手機(jī)就能破解keyChain,本人并不清楚,希望有大神能指教。但個人認(rèn)為,keyChain只是沙盒的升級版,可以存放一些非私密的信息,即使破解也不影響其它用戶,只影響那個破解了的設(shè)備。(比如針對該設(shè)備的一個密鑰)。

可訪問性一般來說,自己的程序只能訪問自己的keychain,相同bundle的程序通過設(shè)置group可以互相共享同組的keychain,從而實(shí)現(xiàn)程序間可以共同訪問一些數(shù)據(jù)。詳細(xì)后面介紹一些我測試下來的經(jīng)驗。

3.如何查詢keyChain

[objc] view plain copy

print?
genericPasswordQuery = [[NSMutableDictionary alloc] init];
[genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];//1
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];//2
******if** (accessGroup !=** nil){
[genericPasswordQuery setObject:accessGroup forKey:(
id)kSecAttrAccessGroup];//3
}
[genericPasswordQuery setObject:(
id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];//4
[genericPasswordQuery setObject:(
id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];//5
******NSDictionary
tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
******NSMutableDictionary
* outDictionary =* nil;
******if
(SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef )&outDictionary) == noErr){//6
//found and outDicitionary is not nil
}
else*{
//not found
}

1.設(shè)置Class值,每個Class對應(yīng)的都有不同的參數(shù)類型

2.用戶確定的參數(shù),一般是程序中使用的類別,比如說是"Password"或"Account Info",作為search的主力條件
3.設(shè)置Group,如果不同程序都擁有這個組,那么不同程序間可以共享這個組的數(shù)據(jù)
4.只返回第一個匹配數(shù)據(jù),查詢方法使用,還有值kSecMatchLimitAll
5.返回數(shù)據(jù)為CFDicitionaryRef,查詢方法使用
6.執(zhí)行查詢方法,判斷返回值
eg:這個是none-ARC的代碼哦!ARC情況下會有bridge提示。

4.類型轉(zhuǎn)換
介紹增刪改方法調(diào)用前,先介紹轉(zhuǎn)換方法,如何將NSDictionary轉(zhuǎn)換成KeyChain方法可以設(shè)置的Dicitionary,一般在寫程序過程中,應(yīng)該盡量避免直接訪問KeyChain,一般會創(chuàng)建一個NSDictionary來同步對應(yīng)的數(shù)據(jù),所以兩者需要做轉(zhuǎn)換。

[objc] view plain copy

print?
//data to secItem

  • (NSMutableDictionary )dictionaryToSecItemFormat:(NSDictionary* *)dictionaryToConvert
    {
    // Create a dictionary to return populated with the attributes and data.
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

    //設(shè)置kSecClass
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    //將Dictionary里的kSecValueData(一般就是這個keyChain里主要內(nèi)容,比如說是password),NSString轉(zhuǎn)換成NSData
    NSString passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];
    [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(
    id*)kSecValueData];
    return returnDictionary;
    }
    //secItem to data

  • (NSMutableDictionary )secItemFormatToDictionary:(NSDictionary* *)dictionaryToConvert
    {
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

    // Add the proper search key and class attribute.
    [returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

    // Acquire the password data from the attributes.
    NSData passwordData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (
    CFTypeRef* )&passwordData) == noErr)
    {
    // 刪除多余的kSecReturnData數(shù)據(jù)
    [returnDictionary removeObjectForKey:(
    id*)kSecReturnData];

      // 對應(yīng)前面的步驟,將數(shù)據(jù)從NSData轉(zhuǎn)成NSString  
      **NSString** *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length]  
                                                   encoding:NSUTF8StringEncoding] autorelease];  
      [returnDictionary setObject:password forKey:(**id**)kSecValueData];  
    

    }
    else
    {
    NSAssert(NO, @"Serious error, no matching item found in the keychain.\n");
    }
    [passwordData release];
    return returnDictionary;
    }

5.增刪改
用代碼來說明

[objc] view plain copy

print?

  • (void)writeToKeychain
    {
    NSDictionary attributes = NULL;
    NSMutableDictionary updateItem = NULL;
    OSStatus result;
    //判斷是增還是改
    if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (
    CFTypeRef** )&attributes) == noErr)
    {
    // First we need the attributes from the Keychain.
    updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
    // Second we need to add the appropriate search key/values.
    [updateItem setObject:[genericPasswordQuery objectForKey:(
    id)kSecClass] forKey:(id)kSecClass];
    // Lastly, we need to set up the updated attribute list being careful to remove the class.
    NSMutableDictionary tempCheck = [
    self** dictionaryToSecItemFormat:keychainItemData];
    //刪除kSecClass update不能update該字段,否則會報錯
    [tempCheck removeObjectForKey:(id)kSecClass];
    //參數(shù)1表示search的,參數(shù)2表示需要更新后的值
    result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
    }else{
    //增加
    result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
    }
    }

刪除很簡單,就不寫注釋了
[objc] view plain copy

print?

  • (void)resetKeychainItem
    {
    OSStatus junk = noErr;
    if (!keychainItemData)
    {
    self.keychainItemData = [[NSMutableDictionary alloc] init];
    }
    else if (keychainItemData)
    {
    NSMutableDictionary tempDictionary = [self* dictionaryToSecItemFormat:keychainItemData];
    junk = SecItemDelete((CFDictionaryRef)tempDictionary);
    NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." );
    }

    // Default attributes for keychain item.
    [keychainItemData setObject:@"" forKey:(id)kSecAttrAccount];
    [keychainItemData setObject:@"" forKey:(id)kSecAttrLabel];
    [keychainItemData setObject:@"" forKey:(id)kSecAttrDescription];

    // Default data for keychain item.
    [keychainItemData setObject:@"" forKey:(id)kSecValueData];
    }

二.Group的配置
配置Target的Code Signing Entitlements.

配置該文件

可以配置一個Array列表,表示該程序可以支持多個group
這樣就可以在創(chuàng)建secItem時候添加kSecAttrAccessGroup了。
經(jīng)過測試有以下經(jīng)驗同大家分享:
1.相同bundle下生成的程序都可以共享相同group的keyChain.
相同bundle解釋下就是:比如:2個程序分別使用的provision對應(yīng)bundle是com.jv.key1和com.jv.key2,那你配置文件肯定是{Identifer}.com.jv.{name},其中identifer是蘋果生成的隨機(jī)串號,可以在申請證書時看到,復(fù)制過來即可,name可以自己取,程序中指定屬于哪個Group即可。
2.如果你在 addkey時,沒有指定group,則會默認(rèn)添加你keychain-access-groups里第一個group,如果你沒有設(shè)置Entitlements,則默認(rèn)使用對應(yīng)的程序的bundle name,比如com.jv.key1,表示只能給自己程序使用。
3.如果你程序添加的group并不存在你的配置文件中,程序會奔潰,表示無法添加。因此你只能添加你配置文件中支持的keychain。

參考資料:
蘋果文檔:
Keychain Services Reference

Certificate, Key, and Trust Services Programming Guide
Keychain Services Programming Guide

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

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

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