Keychain 与数据加密
问题
iOS 中如何安全存储敏感数据?
答案
Keychain
Keychain 是 iOS 提供的系统级加密存储,存储在硬件安全芯片(Secure Enclave)保护下:
import Security
// 存储
func saveToKeychain(key: String, value: String) -> Bool {
guard let data = value.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
}
// 读取
func readFromKeychain(key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true
]
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)
}
CryptoKit 加密
import CryptoKit
// AES-GCM 对称加密
let key = SymmetricKey(size: .bits256)
let plaintext = "敏感数据".data(using: .utf8)!
let sealedBox = try AES.GCM.seal(plaintext, using: key)
let ciphertext = sealedBox.combined!
// 解密
let openedBox = try AES.GCM.SealedBox(combined: ciphertext)
let decrypted = try AES.GCM.open(openedBox, using: key)
// SHA256 哈希
let hash = SHA256.hash(data: plaintext)
存储安全等级
| 方式 | 安全级别 | 适合存储 |
|---|---|---|
| UserDefaults | ❌ 明文 plist | 用户偏好 |
| 文件系统 | ⚠️ 需手动加密 | 普通文件 |
| Keychain | ✅ 系统加密 | Token、密码、密钥 |
| Secure Enclave | ✅✅ 硬件级 | 生物识别密钥 |
常见面试问题
Q1: Keychain 和 UserDefaults 存 Token 的区别?
答案:UserDefaults 以明文 plist 存储,越狱设备可直接读取。Keychain 由系统加密保护,App 卸载后数据仍保留(除非指定 ThisDeviceOnly),安全性远高于 UserDefaults。