KeychainAccess 是一个简单的 Swift Keychain 封装,可在 iOS 和 macOS 上使用。 使 Keychain API 的使用变得非常容易,并且更适合在 Swift 中使用。
let keychain = Keychain(service: "com.example.github-token")
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(server: "https://github.com", protocolType: .https)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
let keychain = Keychain(server: "https://github.com", protocolType: .https)
let keychain = Keychain(server: "https://github.com", protocolType: .https, authenticationType: .htmlForm)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin")
keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
do {
try keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
}
catch let error {
print(error)
}
let token = keychain["kishikawakatsumi"]
let token = keychain[string: "kishikawakatsumi"]
let secretData = keychain[data: "secret"]
let token = try? keychain.get("kishikawakatsumi")
let token = try? keychain.getString("kishikawakatsumi")
let data = try? keychain.getData("kishikawakatsumi")
keychain["kishikawakatsumi"] = nil
do {
try keychain.remove("kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
let keychain = Keychain(server: "https://github.com", protocolType: .https)
do {
try keychain
.label("github.com (kishikawakatsumi)")
.comment("github access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
let keychain = Keychain()
let persistentRef = keychain[attributes: "kishikawakatsumi"]?.persistentRef
...
let keychain = Keychain()
let creationDate = keychain[attributes: "kishikawakatsumi"]?.creationDate
...
let keychain = Keychain()
do {
let attributes = try keychain.get("kishikawakatsumi") { $0 }
print(attributes?.comment)
print(attributes?.label)
print(attributes?.creator)
...
} catch let error {
print("error: \(error)")
}
let keychain = Keychain()
if let attributes = keychain[attributes: "kishikawakatsumi"] {
print(attributes.comment)
print(attributes.label)
print(attributes.creator)
}
提供流畅的接口
let keychain = Keychain(service: "com.example.github-token")
.label("github.com (kishikawakatsumi)")
.synchronizable(true)
.accessibility(.afterFirstUnlock)
let keychain = Keychain(service: "com.example.github-token")
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.afterFirstUnlock)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.accessibility(.afterFirstUnlock)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
let keychain = Keychain(service: "com.example.github-token")
.accessibility(.whenUnlocked)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.accessibility(.whenUnlocked)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
let keychain = Keychain(service: "com.example.github-token")
.synchronizable(true)
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain
.synchronizable(true)
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
print("error: \(error)")
}
任何需要身份验证的操作都必须在后台线程中运行。
如果在主线程中运行,UI 线程将锁定,以便系统尝试显示身份验证对话框。
要使用 Face ID,请将 NSFaceIDUsageDescription
键添加到您的 Info.plist
文件中
如果要存储受 Touch ID 保护的 Keychain 项目,请指定 accessibility
和 authenticationPolicy
属性。
let keychain = Keychain(service: "com.example.github-token")
DispatchQueue.global().async {
do {
// Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked`
try keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny])
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
// Error handling if needed...
}
}
与添加时相同。
如果尝试添加的项目可能已存在并受到保护,请勿在主线程中运行。 因为更新受保护的项目需要身份验证。
此外,如果要更新时显示自定义身份验证提示消息,请指定 authenticationPrompt
属性。 如果该项目未受保护,则 authenticationPrompt
参数将被忽略。
let keychain = Keychain(service: "com.example.github-token")
DispatchQueue.global().async {
do {
// Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked`
try keychain
.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny])
.authenticationPrompt("Authenticate to update your access token")
.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
} catch let error {
// Error handling if needed...
}
}
与获取普通项目的方式相同。 如果您尝试获取的项目受到保护,它将自动显示 Touch ID 或密码身份验证。
如果要显示自定义身份验证提示消息,请指定 authenticationPrompt
属性。 如果该项目未受保护,则 authenticationPrompt
参数将被忽略。
let keychain = Keychain(service: "com.example.github-token")
DispatchQueue.global().async {
do {
let password = try keychain
.authenticationPrompt("Authenticate to login to server")
.get("kishikawakatsumi")
print("password: \(password)")
} catch let error {
// Error handling if needed...
}
}
与删除普通项目的方式相同。 删除 Keychain 项目时,无法显示 Touch ID 或密码身份验证。
let keychain = Keychain(service: "com.example.github-token")
do {
try keychain.remove("kishikawakatsumi")
} catch let error {
// Error handling if needed...
}
共享 Web 凭据是一种编程接口,使本机 iOS 应用程序能够与其网站对应部分共享凭据。 例如,用户可以在 Safari 中登录网站,输入用户名和密码,并使用 iCloud Keychain 保存这些凭据。 稍后,用户可能会运行来自同一开发人员的本机应用程序,而不是要求用户重新输入用户名和密码,共享 Web 凭据使其可以访问先前在 Safari 中输入的凭据。 用户还可以创建新帐户、更新密码或从应用程序中删除她的帐户。 这些更改随后会被保存并被 Safari 使用。
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/
let keychain = Keychain(server: "https://www.kishikawakatsumi.com", protocolType: .HTTPS)
let username = "kishikawakatsumi@mac.com"
// First, check the credential in the app's Keychain
if let password = try? keychain.get(username) {
// If found password in the Keychain,
// then log into the server
} else {
// If not found password in the Keychain,
// try to read from Shared Web Credentials
keychain.getSharedPassword(username) { (password, error) -> () in
if password != nil {
// If found password in the Shared Web Credentials,
// then log into the server
// and save the password to the Keychain
keychain[username] = password
} else {
// If not found password either in the Keychain also Shared Web Credentials,
// prompt for username and password
// Log into server
// If the login is successful,
// save the credentials to both the Keychain and the Shared Web Credentials.
keychain[username] = inputPassword
keychain.setSharedPassword(inputPassword, account: username)
}
}
}
Keychain.requestSharedWebCredential { (credentials, error) -> () in
}
生成 Safari 自动填充使用的相同格式的强随机密码 (xxx-xxx-xxx-xxx)。
let password = Keychain.generatePassword() // => Nhu-GKm-s3n-pMx
将 com.apple.developer.associated-domains 授权添加到您的应用程序。 此授权必须包括您要与之共享凭据的所有域。
将 apple-app-site-association 文件添加到您的网站。 此文件必须包含站点要与之共享凭据的所有应用程序的应用程序标识符,并且必须正确签名。
安装应用程序后,系统会下载并验证其每个关联域的站点关联文件。 如果验证成功,则该应用程序将与该域关联。
更多细节
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/
let keychain = Keychain(server: "https://github.com", protocolType: .https)
print("\(keychain)")
=>
[
[authenticationType: default, key: kishikawakatsumi, server: github.com, class: internetPassword, protocol: https]
[authenticationType: default, key: hirohamada, server: github.com, class: internetPassword, protocol: https]
[authenticationType: default, key: honeylemon, server: github.com, class: internetPassword, protocol: https]
]
let keychain = Keychain(server: "https://github.com", protocolType: .https)
let keys = keychain.allKeys()
for key in keys {
print("key: \(key)")
}
=>
key: kishikawakatsumi
key: hirohamada
key: honeylemon
let keychain = Keychain(server: "https://github.com", protocolType: .https)
let items = keychain.allItems()
for item in items {
print("item: \(item)")
}
=>
item: [authenticationType: Default, key: kishikawakatsumi, server: github.com, class: InternetPassword, protocol: https]
item: [authenticationType: Default, key: hirohamada, server: github.com, class: InternetPassword, protocol: https]
item: [authenticationType: Default, key: honeylemon, server: github.com, class: InternetPassword, protocol: https]
如果遇到以下错误,则需要添加 Keychain.entitlements
。
OSStatus error:[-34018] Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements.
操作系统 | Swift | |
---|---|---|
v1.1.x | iOS 7+, macOS 10.9+ | 1.1 |
v1.2.x | iOS 7+, macOS 10.9+ | 1.2 |
v2.0.x | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 |
v2.1.x | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 |
v2.2.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1 |
v2.3.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1, 2.2 |
v2.4.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.2, 2.3 |
v3.0.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 3.x |
v3.1.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2 |
v3.2.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.0 |
v4.0.x | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.1 |
v4.1.x | iOS 8+, macOS 10.9+, watchOS 3+, tvOS 9+, Mac Catalyst 13+ | 4.0, 4.1, 4.2, 5.1 |
KeychainAccess 可通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile
use_frameworks!
pod 'KeychainAccess'
KeychainAccess 可通过 Carthage 获得。 要安装它,只需将以下行添加到您的 Cartfile
github "kishikawakatsumi/KeychainAccess"
KeychainAccess 也可通过 Swift Package Manager 获得。
选择 File > Add Packages... > Add Package Dependency...
,
首先,创建 Package.swift
,其包声明包括
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "MyLibrary",
products: [
.library(name: "MyLibrary", targets: ["MyLibrary"]),
],
dependencies: [
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "3.0.0"),
],
targets: [
.target(name: "MyLibrary", dependencies: ["KeychainAccess"]),
]
)
然后,输入
$ swift build
Lib/KeychainAccess.xcodeproj
添加到您的项目KeychainAccess.framework
与您的目标链接Copy Files Build Phase
以将框架包含到您的应用程序捆绑包中请参阅 iOS 示例项目 作为参考。
kishikawa katsumi, kishikawakatsumi@mac.com
KeychainAccess 在 MIT 许可下可用。 有关更多信息,请参见 LICENSE 文件。