iOS第七十三篇:Keychain使用指南
Keychain 是 iOS 和 macOS 提供的安全存储服务用户密码加密密钥证书支付信息其他敏感数据加密存储在设备上应用卸载后仍然保留(除非手动删除)受系统级安全保护。
·
iOS Keychain 使用指南
目录
什么是 Keychain?
Keychain 是 iOS 和 macOS 提供的安全存储服务,用于保存敏感信息:
- 用户密码
- 加密密钥
- 证书
- 支付信息
- 其他敏感数据
与 UserDefaults 不同,Keychain 数据:
- 加密存储在设备上
- 应用卸载后仍然保留(除非手动删除)
- 受系统级安全保护
为什么使用 Keychain?
- 安全性:硬件加密保护(Secure Enclave)
- 持久性:应用卸载后数据不会丢失
- 跨应用共享:通过 App Groups 实现
- 符合隐私要求:安全存储用户凭证
- 生物识别集成:支持 Face ID/Touch ID 验证
Keychain 核心概念
Keychain 项结构
每个 Keychain 条目包含:
- kSecClass:数据类型(密码/证书/密钥等)
- kSecAttrAccount:账户标识(通常用户名)
- kSecAttrService:服务标识(通常 bundle ID)
- kSecValueData:加密存储的实际数据
- kSecAttrAccessible:访问策略(见下表)
访问策略(Accessibility)
选项 | 描述 |
---|---|
kSecAttrAccessibleWhenUnlocked |
设备解锁时可访问(推荐) |
kSecAttrAccessibleAfterFirstUnlock |
设备首次解锁后可访问 |
kSecAttrAccessibleWhenPasscodeSet |
设备设置密码时可访问 |
kSecAttrAccessibleWhenUnlockedThisDeviceOnly |
仅当前设备解锁可访问(不备份) |
常用 Keychain 类(kSecClass)
kSecClassGenericPassword
:通用密码(最常用)kSecClassInternetPassword
:互联网密码kSecClassCertificate
:证书kSecClassKey
:加密密钥
Keychain 操作指南
提示:实际开发建议封装 Keychain 工具类(示例代码)
添加数据到 Keychain
import Security
func addItemToKeychain(account: String, password: String) -> Bool {
guard let passwordData = password.data(using: .utf8) else { return false }
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: Bundle.main.bundleIdentifier!,
kSecValueData: passwordData,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
查询 Keychain 数据
func getPasswordFromKeychain(account: String) -> String? {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: Bundle.main.bundleIdentifier!,
kSecReturnData: true,
kSecMatchLimit: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess,
let data = result as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
更新 Keychain 数据
func updateKeychainItem(account: String, newPassword: String) -> Bool {
guard let newPasswordData = newPassword.data(using: .utf8) else { return false }
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: Bundle.main.bundleIdentifier!
]
let attributes: [CFString: Any] = [
kSecValueData: newPasswordData
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
return status == errSecSuccess
}
删除 Keychain 数据
func deleteKeychainItem(account: String) -> Bool {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: Bundle.main.bundleIdentifier!
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
Keychain 共享(App Groups)
实现同一开发者账号下的多个应用共享 Keychain 数据:
-
开启 App Groups
- 在 Xcode 项目中:
- 添加 Capability → App Groups
- 创建新的 App Group ID(格式:
group.com.yourcompany.appgroup
)
- 在 Xcode 项目中:
-
使用共享 Keychain:
let sharedGroup = "group.com.yourcompany.appgroup" let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: "sharedAccount", kSecAttrService: "SharedService", kSecAttrAccessGroup: sharedGroup, // 关键设置 kSecValueData: data ]
最佳实践与注意事项
-
合理选择访问策略:
- 使用
kSecAttrAccessibleWhenUnlocked
作为默认选项 - 敏感数据使用
ThisDeviceOnly
防止备份
- 使用
-
错误处理:
let status = SecItemAdd(...) if status == errSecDuplicateItem { // 处理重复项 } else if status == errSecAuthFailed { // 认证失败 }
-
数据组织:
- 使用
kSecAttrService
区分数据类型 - 避免存储大体积数据(Keychain 不是数据库)
- 使用
-
生物识别集成:
let context = LAContext() context.touchIDAuthenticationAllowableReuseDuration = 10 let query: [CFString: Any] = [ kSecUseAuthenticationContext: context, // 其他参数... ]
-
线程安全:
- Keychain 操作是同步的
- 在后台队列执行耗时操作
常见问题解决
错误代码对照表
错误代码 | 描述 |
---|---|
errSecSuccess (0) |
操作成功 |
errSecDuplicateItem (-25299) |
项已存在 |
errSecItemNotFound (-25300) |
项不存在 |
errSecAuthFailed (-25293) |
认证失败 |
调试技巧
-
重置模拟器 Keychain:
# 终端执行 xcrun simctl erase all
-
查看 Keychain 内容:
- 使用 Keychain Access 应用(macOS)
- 过滤器选择 “iCloud” 或 “登录”
-
真机测试时:
- 确保开启 Keychain 共享能力
- 检查 App Group 配置
完整工具类示例
import Security
struct KeychainManager {
static let service = Bundle.main.bundleIdentifier!
enum KeychainError: Error {
case duplicateItem
case unknown(OSStatus)
}
static func save(_ data: Data, account: String) throws {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: service,
kSecValueData: data,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked
]
let status = SecItemAdd(query as CFDictionary, nil)
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateItem
}
guard status == errSecSuccess else {
throw KeychainError.unknown(status)
}
}
static func get(account: String) -> Data? {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: service,
kSecReturnData: true,
kSecMatchLimit: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
return status == errSecSuccess ? result as? Data : nil
}
static func delete(account: String) -> Bool {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrService: service
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
}
通过本指南,您应该掌握了 Keychain 的核心概念、操作方法和最佳实践。Keychain 是 iOS 安全体系中至关重要的组件,正确使用它可以显著提升您应用的 security posture。
更多推荐
所有评论(0)