Swift 钥匙串访问
在 iOS 和 macOS 开发中,数据持久化是一个非常重要的概念。我们通常需要存储一些敏感信息,例如用户密码、API 密钥等。为了确保这些数据的安全性,苹果提供了 钥匙串(Keychain) 服务。钥匙串是一个安全的存储系统,专门用于保存敏感信息。
什么是钥匙串?
钥匙串是苹果提供的一个加密存储系统,用于保存密码、证书、密钥等敏感数据。与 UserDefaults 或文件系统不同,钥匙串中的数据是加密的,并 且只能通过特定的 API 访问。这使得钥匙串成为存储敏感信息的理想选择。
钥匙串不仅限于存储密码,还可以存储其他类型的敏感数据,例如加密密钥、证书等。
钥匙串的基本概念
在开始使用钥匙串之前,我们需要了解一些基本概念:
-
钥匙串项(Keychain Item):钥匙串中的每一项数据都称为一个钥匙串项。每个钥匙串项都有一个唯一的标识符(
kSecAttrService
和kSecAttrAccount
),用于区分不同的项。 -
钥匙串访问控制(Keychain Access Control):你可以为每个钥匙串项设置访问控制策略,例如是否需要用户输入密码才能访问该项。
-
钥匙串类(Keychain Class):钥匙串项可以分为不同的类,例如
kSecClassGenericPassword
(通用密码)、kSecClassInternetPassword
(互联网密码)等。
使用 Swift 访问钥匙串
在 Swift 中,我们可以使用 Security
框架来访问钥匙串。以下是一个简单的示例,展示如何将数据保存到钥匙串中。
保存数据到钥匙串
import Security
func saveToKeychain(service: String, account: String, password: String) -> Bool {
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecValueData as String: passwordData
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
在这个示例中,我们定义了一个 saveToKeychain
函数,它将密码保存到钥匙串中。我们使用 kSecClassGenericPassword
类来存储通用密码,并通过 kSecAttrService
和 kSecAttrAccount
来标识该项。
从钥匙串中读取数据
要从钥匙串中读取数据,我们可以使用以下代码:
func readFromKeychain(service: String, account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess, let passwordData = item as? Data else {
return nil
}
return String(data: passwordData, encoding: .utf8)
}
在这个函数中,我们使用 SecItemCopyMatching
来查询钥匙串中的数据。如果查询成功,我们将返回存储的密码。
删除钥匙串中的数据
如果你需要删除钥匙串中的某项数据,可以使用以下代码:
func deleteFromKeychain(service: String, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}