KeychainAccess 4.2.2

KeychainAccess 4.2.2

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2021年3月
SPM支持 SPM

kishikawa katsumi 维护。




KeychainAccess

CI Status Carthage compatible SPM supported Version Platform

KeychainAccess 是一个简单的 Swift 包装器,用于 Keychain,可在 iOS 和 OS X 上运行。使使用 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)

🔑添加项目

subscripting

for String
keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
for NSData
keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin")

set method

keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")

error handling

do {
    try keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
}
catch let error {
    print(error)
}

🔑获取项目

下标索引

对 String(如果值是 NSData,尝试转换为 String)
let token = keychain["kishikawakatsumi"]
let token = keychain[string: "kishikawakatsumi"]
对 NSData
let secretData = keychain[data: "secret"]

获取方法

作为 String
let token = try? keychain.get("kishikawakatsumi")
let token = try? keychain.getString("kishikawakatsumi")
作为 NSData
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)")
}

🔑获取其他属性

PersistentRef

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)
}

🔑配置(无障碍功能、共享、iCloud 同步)

提供流畅的接口

let keychain = Keychain(service: "com.example.github-token")
    .label("github.com (kishikawakatsumi)")
    .synchronizable(true)
    .accessibility(.afterFirstUnlock)

无障碍功能

默认无障碍功能匹配背景应用程序(=kSecAttrAccessibleAfterFirstUnlock)
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")

🔄 使用iCloud同步密钥链项目

创建实例
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)")
}

🌀 Touch ID (Face ID)集成

需要认证的操作必须在后台线程中运行。
如果在主线程中运行,UI线程将会锁定,以便系统尝试显示认证对话框。

要使用Face ID,请将NSFaceIDUsageDescription键添加到你的Info.plist文件中。

🔐添加受Touch ID (Face ID)保护的项

如果要存储受Touch ID protected Keychain项,请指定accessibilityauthenticationPolicy属性。

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: .userPresence)
            .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
    } catch let error {
        // Error handling if needed...
    }
}

🔐更新受Touch ID (Face ID)保护的项

与添加时相同的方式。

如果存在您要尝试添加的项已经存在且受保护的可能性,请在主线程外运行。 因为更新受保护项目需要认证。

另外,当更新时还想显示自定义认证提示消息,指定一个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: .userPresence)
            .authenticationPrompt("Authenticate to update your access token")
            .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
    } catch let error {
        // Error handling if needed...
    }
}

🔐获取受Touch ID (Face ID)保护的项

与获取普通项时相同的方式。如果尝试获取的项是受保护的,将自动显示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...
    }
}

🔐删除受Touch ID (Face ID)保护的项

与删除普通项时相同的方式。删除Keychain项时没有显示Touch ID或密码认证的方法。

let keychain = Keychain(service: "com.example.github-token")

do {
    try keychain.remove("kishikawakatsumi")
} catch let error {
    // Error handling if needed...
}

🔑 共享Web凭证

共享Web凭证是一个程序接口,允许原生iOS应用与其相应的网站共享凭证。例如,用户可以在Safari中登录网站,输入用户名和密码,并使用iCloud密钥链保存这些凭证。稍后,用户可以运行同一开发者的原生应用,而无需用户重新输入用户名和密码,共享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 = "[email protected]"

// 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

如何设置共享Web凭证

  1. 将com.apple.developer.associated-domains许可添加到您的应用中。此许可必须包括您想要共享凭证的所有域。

  2. 将apple-app-site-association文件添加到您的网站中。此文件必须包含网站想要共享凭证的所有应用的标识符,并且必须正确签名。

  3. 当应用安装时,系统将下载并验证其关联的每个域的网站关联文件。如果验证成功,应用将与域名相关联。

更多详细信息
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.

Screen Shot 2019-10-27 at 8 08 50

需求

操作系统 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

安装

CocoaPods

KeychainAccess 通过 CocoaPods 提供。要安装它,只需在您的 Podfile 中添加以下行

use_frameworks!
pod 'KeychainAccess'

Carthage

KeychainAccess 也通过 Carthage 提供。要安装它,只需在您的 Cartfile 中添加以下行

github "kishikawakakumi/KeychainAccess"

Swift Package Manager

KeychainAccess 同样也通过 Swift Package Manager 提供。

Xcode

选择 文件 > Swift 包 > 添加包依赖...,

CLI

首先,创建包含其包声明的 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

将手动添加到您的项目中

  1. Lib/KeychainAccess.xcodeproj 添加到项目中
  2. KeychainAccess.framework 链接到您的目标
  3. Copy Files Build Phase添加到您的应用程序包中,包括框架

请参考iOS示例项目

作者

kishikawa katsumi, [email protected]

许可证

KeychainAccess根据MIT许可证提供。有关更多信息,请参阅LICENSE文件。