iOS Keychain鑰匙串,應用間數據共享打造iOS上的全家桶


Demo先行:https://github.com/rayshen/GIKeychainGroupDemo

該demo里有2個工程,你先運行任何一個會存儲一個值,再運行另一個會訪問之前的app存儲的值,並修改。

官方:https://developer.apple.com/library/ios/samplecode/GenericKeychain/Introduction/Intro.html

 

之前博客使用過Keychain,實現了數據刪除APP后還能保存,但是並沒有實現APP間的共享。

實現APP間的數據共享,主要依賴於在數據存入鑰匙串時,使用同一個鑰匙串條目。

主要分為兩部分:

1.賦予應用對某個鑰匙串條目的訪問權限。

2.寫入時配置鑰匙串條目,對kSecAttrAccessGroup的值進行設置。

 

一、APP對鑰匙串的訪問權限:

(1)未對應用APP的entitlement(授權)進行配置時,APP使用鑰匙串存儲時,會默認存儲在自身BundleID的條目下。

(2)對APP的entitlement(授權)進行配置后,說明APP有了對某個條目的訪問權限。

 

鑰匙串的可視化效果可參見Mac的APP-鑰匙串訪問。

 

APP鑰匙串訪問權限的配置方法:(這里XXXXX模擬器隨意,但真機必須為自己開發者賬號ID,否則無法通過編譯)

1.新建一個Plist文件,在Plist中的數組中添加可以訪問的條目的名字(如KeychainAccessGroups.plist),結構如下:

Plist代碼:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>keychain-access-groups</key>
    <array>
        <string>XXXXX.GrassInfoAppFamily</string>
    </array>
</dict>
</plist>

2.在Build-setting中進行配置,搜索entitlement,注意路徑別配置錯:

 

二、APP對鑰匙串的操作:

鑰匙串的操作接口都位於Security.framework框架下,它是一個sqlite數據庫,位於/private/var/Keychains/keychain-2.db,其保存的所有數據都是加密過的。

其過程可以總結為:

1.配置查詢字典,格式是NSMutableDictionary,需要配置的內容下次再分析,功能就相當於寫一句SQL一樣。

2.進行增(SecItemAdd)、刪(SecItemDelete)、改(SecItemUpdate)、查(SecItemCopyMatching)。

代碼Demo里面有,這里以增為例,下面有2個語句,一個是增加到自身BundleID的鑰匙串條目,一個是增加到共享的條目中。

//創建一個基本的查詢字典
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
            service, (__bridge id)kSecAttrService,
            service, (__bridge id)kSecAttrAccount,
            (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
            nil];
}

+ (void)addKeychainData:(id)data forKey:(NSString *)key{
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
    //Delete old item before add new item
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}

+(void)addShareKeyChainData:(id)data forKey:(NSString *)key{
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
    [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup];
    //Delete old item before add new item
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}

函數  [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup] 的配置,就是指定了這次寫入時的鑰匙串條目,不寫入時默認為plist配置文件里第一個條目。

在查詢中,也可以對查詢的鑰匙串條目進行配置,默認會對所有有權限的條目進行搜索

 

三、keychain的組成:

參考博客:http://my.oschina.net/w11h22j33/blog/206713

每一個keyChain的組成如圖,整體是一個字典結構.
1.kSecClass key 定義屬於那一種類型的keyChain
2.不同的類型包含不同的Attributes,這些attributes定義了這個item的具體信息
3.每個item可以包含一個密碼項來存儲對應的密碼

 

對於最常用密碼類型,我們應該如下配置

[wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];//class

[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];//key

[wrapper setObject:@"password"forKey:(id)kSecValueData];//value

[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];

kSecAttrAccessiblein變量用來指定這個應用合適需要訪問這個數據。我們需要對這個選項特別注意,並且使用最嚴格的選項。這個鍵(key)可以設置6種值。

你可以參考以下:

 

四、安全

前面說到,APP能夠訪問的keychain數據是通過其entitlements文件指定的。

但是!!如果使用帶有一個*通配符的entitlments,因此它能夠訪問keychain中的所有條目。。或者,如果用一個包含所有訪問組(access group)的entitlements文件,也能夠訪問所有的keychain數據。

但以上僅限模擬器,在真機調試時,假如你plist里的條目,和自己調試文件(pro file)的ID不一致時(比如:EC0880A1.company),進行真機編譯的時候是會報錯的。

所以真正要保證全家桶,還需要保證這些APP在同一個開發者ID下才行。 

 

前面關於在未配置keychain-access-group的情況下,我參考別人說是默認存儲在該BundleID的條目下,但實測並非存儲在該條目下。具體為什么我也暫時不清楚。但是可以肯定的是,在iOS的沙盒機制中,你默認存儲的條目下的K-V,其他APP是無法訪問的(除了模擬器中的通配符*,真機是不行的)。

 

關於Keychain還有很多值得挖掘的,比如具體在sqlite數據庫的表單存儲方式等,有錯歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM