Keychain
问题
Keychain 的原理是什么?如何安全存储敏感信息?
答案
Keychain vs UserDefaults
| Keychain | UserDefaults | |
|---|---|---|
| 加密 | ✅ 系统硬件加密 | ❌ 明文 |
| 删除 App 后 | ⚠️ 数据保留 | 数据删除 |
| 访问控制 | 可设置生物识别 | 无 |
| 性能 | 较慢(加密解密) | 快 |
| 适用 | 密码、Token、证书 | 设置、偏好 |
使用 Security 框架
class KeychainManager {
static func save(key: String, data: Data) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data
]
SecItemDelete(query as CFDictionary) // 先删除旧值
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
static func load(key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
return status == errSecSuccess ? result as? Data : nil
}
static func delete(key: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key
]
SecItemDelete(query as CFDictionary)
}
}
// 存储 Token
let token = "eyJhbGciOiJIUzI1NiJ9..."
KeychainManager.save(key: "accessToken", data: token.data(using: .utf8)!)
推荐使用封装库
原生 Keychain API 基于 C 语言字典,使用繁琐。推荐使用 KeychainAccess 等封装库。
常见面试问题
Q1: 删除 App 后 Keychain 数据还在吗?
答案:默认情况下 Keychain 数据在 App 删除后仍然保留(直到 Keychain 条目被显式删除或设备重置)。如果需要随 App 删除,可以在首次启动时检查并清理。
Q2: Keychain 可以在 App 之间共享数据吗?
答案:可以。通过 Keychain Access Groups(需要在 Entitlements 中配置相同的 group),同一开发者的不同 App 可以共享 Keychain 数据。