
Keychain 介紹
Keychain Services 是 macOS 和 iOS 都提供一種安全地存儲敏感信息的工具,比如:”網絡密碼:用于保存訪問服務器或者網站,通用密碼:用來保存應用程序或者數據庫密碼“。與此同時,用于認證的證書、密鑰和身份信息,也可以存儲在 Keychain 中。Keychain Services 的安全機制保證了存儲這些敏感信息不會被竊取。簡單說來,Keychain 就是一個安全容器。另附 Keychain Services - Apple Developer Documentation。
PS: 在 iOS 中 Keychian 依賴用于簽名的 provisioning profile 描述文件,確保發(fā)布不同版本的時候,使用同一個 provisioning profile 文件。

Keychain 的結構
Keychain 可以包含任意數量的 Keychain Item。每一個 Keychain Item 包含數據和一組屬性。對于一個需要保護的 Keychain Item,比如密碼或者私鑰(用于加密或者解密的 String 字節(jié))數據是加密的,會被 Keychain 保護起來的;對于無需保護的 Keychain Item,例如: ”證書,數據未被加密“。
跟 Keychain Item 有關系的取決于 Item 的類型;應用程序中最常用的是網絡密碼(Internet passwrods)和普通的密碼。正如你所想的,網絡密碼像安全域(security domain)、協議和路徑等一些屬性。在 macOS 中,當 Keychain 被鎖的時候,加密的 Item 沒辦法訪問,如果你想要該問被鎖的 Item,就會彈出一個對話框,需要你輸入對應 Keychain 的密碼。當然,未有密碼的 Keychain 你可以隨時訪問。但在 iOS 中,你只可以訪問你自已的 Keychain Items。
Keychain 的特點
數據并不存放在 App 的 Sanbox 中,即使刪除了 App,資料依然保存在 Keychain 中。如果重新安裝了 App,還可以從 Keychain 獲取數據。
Keychain 的數據可以用過 Group 方式,讓數據可以在 App 間共享。不過得要相同
TeamID。Keychain 的數據是經過加密的。
Keychain 的使用
大多數應用需要用到 Keychain, 都用來添加一個密碼,修改一個已存在 Keychain Item 或者取回密碼。
Keychain 提供了以下的操作:
- SecItemAdd 添加一個 Item
- SecItemUpdate 更新已存在的 Item
- SecItemCopyMatching 搜索一個已存在的 Item
- SecItemDelete 刪除一個 Item
推薦 Swift 開源庫 DYFSwiftKeychain,另外,附上 (Objective-C) 版 DYFKeychain。
寫入數據

- 寫入字符串
let keychain = DYFSwiftKeychain()
keychain.set("User Account Passcode", forKey: "kUserAccPasscode")
- 寫入字節(jié)序列
let keychain = DYFSwiftKeychain()
keychain.set(data, forKey: "kCommSecureCode")
- 寫入布爾值
let keychain = DYFSwiftKeychain()
keychain.set(true, forKey: "kFirstInstalledAndLaunched")
讀取數據
- 讀取字符串
let keychain = DYFSwiftKeychain()
let passcode = keychain.get("kUserAccPasscode")
- 讀取字節(jié)序列
let keychain = DYFSwiftKeychain()
let data = keychain.getData("kCommSecureCode")
- 讀取布爾值
let keychain = DYFSwiftKeychain()
keychain.getBool("kFirstInstalledAndLaunched")
刪除數據
let keychain = DYFSwiftKeychain()
let _ = keychain.delete("kFirstInstalledAndLaunched") // Remove single key.
let _ = keychain.clear() // Delete everything from app's Keychain. Does not work on macOS.
高級選項
1. 鑰匙串Item訪問選項
let keychain = DYFSwiftKeychain()
keychain.set("xxx", forKey:"Key1", withAccess: .accessibleWhenUnlocked)
查看所有可用訪問選項的列表。
2. 與其他設備同步鑰匙串Item
請注意,您不需要在應用程序的目標中啟用 iCloud 或 Keychain 共享功能,使此功能工作。
// The first device
let keychain = DYFSwiftKeychain()
keychain.synchronizable = true
keychain.set("See you tomorrow!", forKey: "key12")
// The second device
let keychain = DYFSwiftKeychain()
keychain.synchronizable = true
let s = keychain.get("key12") // Returns "See you tomorrow!"
我們無法在 macOS 上進行鑰匙串同步工作。
3. 與其他應用程序共享鑰匙串Item
使用accessGroup屬性訪問共享鑰匙串Item。在下面的示例中,我們指定一個訪問組9ZU3R2F3D4.com.omg.myapp.KeychainGroup,它將用于設置、獲取和刪除key1項。
let keychain = DYFSwiftKeychain()
keychain.accessGroup = "9ZU3R2F3D4.com.omg.myapp.KeychainGroup" // Use your own access group.
keychain.set("hello world", forKey: "key1")
keychain.get("key1")
keychain.delete("key1")
keychain.clear()
注:watchOS 2.0與其配對設備之間無法共享鑰匙串item:https://forums.developer.apple.com/thread/5938
3. 數據引用返回
使用asReference:true參數將數據作為引用返回,這是 NEVPNProtocol 協議所需的。
let keychain = DYFSwiftKeychain()
keychain.set(data, forKey: "key1")
keychain.getData("key1", asReference: true)
共享 Keychain 數據
關于共享 Keychain 數據,你可以通過 Group 的方式讓資料可以在 App 間共享,Google 系列的 App (Gmail、Google+、日歷……) 就是通過這樣的方式來記錄使用者登入信息,只要使用者在其中一個 App 中完成登入了,其他的 App 也可以讀取到相同的數據進行登入。
進入 Capabilities,打開 Keychain Sharing 功能

開啟 Keychain Sharing 后,會自動新增一個 Keychain Group,使用的是 Bundle Identifier,同時也會自動新增一個 entitlements 文件,里面也會有一個 Access Group,名為 $(AppIdentifierPrefix)+Your Bundle Identifier。

AppIdentifierPrefix 是開發(fā)者的賬號需要登錄才會有,也就是開發(fā)者證書后小括號內的英文數字組合。使用 $(AppIdentifierPrefix) 只能被同一個開發(fā)者賬號的 App 來存取,以防被有心人盜取。

若其他的 App 也要存取當前 Keychain 的數據,就必需在 Keychain Sharing 開啟后,新增相同的 Keychain Group (Access Group 會根據 Keychain Group 自動新增)。
PS: 需要 Info.plist 新增一個鍵值對 Key: AppIdentifierPrefix,Value: $(AppIdentifierPrefix),方便取得 Identifier Prefix。
總結:寫的有不好的地方,希望大家指出,我會更正,大家有什么看不明白的,可以在評論里面提問,我會盡力解答。另附 Apple 官方 GenericKeychain。
最后,想了解更多詳情,請查看我的 Demo,記得給個 Star,????
Demo ( Objective-C ):戳這里
Demo ( Swift ):戳這里