wireapp/KeychainAccess

Name: KeychainAccess

Owner: Wire Swiss GmbH

Description: Simple Swift wrapper for Keychain that works on iOS and OS X

Forked from: kishikawakatsumi/KeychainAccess

Created: 2017-01-09 15:17:06.0

Updated: 2017-11-05 15:18:20.0

Pushed: 2018-04-12 13:48:47.0

Homepage:

Size: 1309

Language: Swift

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

KeychainAccess

CI Status codecov Carthage compatible Version Platform Swift 3.x Swift 4.0

KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and OS X. Makes using Keychain APIs extremely easy and much more palatable to use in Swift.

:bulb: Features
:book: Usage
:eyes: See also:
:key: Basics
Saving Application Password
keychain = Keychain(service: "com.example.github-token")
hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
Saving Internet Password
keychain = Keychain(server: "https://github.com", protocolType: .https)
hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
:key: Instantiation
Create Keychain for Application Password
keychain = Keychain(service: "com.example.github-token")
wift
keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
Create Keychain for Internet Password
keychain = Keychain(server: "https://github.com", protocolType: .https)
wift
keychain = Keychain(server: "https://github.com", protocolType: .https, authenticationType: .htmlForm)
:key: Adding an item
subscripting for String
hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
wift
hain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
for NSData
hain[data: "secret"] = NSData(contentsOfFile: "secret.bin")
set method
hain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
error handling

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

h let error {
print(error)

:key: Obtaining an item
subscripting for String (If the value is NSData, attempt to convert to String)
token = keychain["kishikawakatsumi"]
wift
token = keychain[string: "kishikawakatsumi"]
for NSData
secretData = keychain[data: "secret"]
get methods as String
token = try? keychain.get("kishikawakatsumi")
wift
token = try? keychain.getString("kishikawakatsumi")
as NSData
data = try? keychain.getData("kishikawakatsumi")
:key: Removing an item
subscripting
hain["kishikawakatsumi"] = nil
remove method

try keychain.remove("kishikawakatsumi")
tch let error {
print("error: \(error)")

:key: Set Label and Comment
keychain = Keychain(server: "https://github.com", protocolType: .https)

try keychain
    .label("github.com (kishikawakatsumi)")
    .comment("github access token")
    .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
tch let error {
print("error: \(error)")

:key: Obtaining Other Attributes
PersistentRef
keychain = Keychain()
persistentRef = keychain[attributes: "kishikawakatsumi"].persistentRef

Creation Date
keychain = Keychain()
creationDate = keychain[attributes: "kishikawakatsumi"].creationDate

All Attributes
keychain = Keychain()

let attributes = try keychain.get("kishikawakatsumi") { $0 }
print(attributes.comment)
print(attributes.label)
print(attributes.creator)
...
tch let error {
print("error: \(error)")

subscripting
keychain = Keychain()
attributes = keychain[attributes: "kishikawakatsumi"]
t(attributes.comment)
t(attributes.label)
t(attributes.creator)
:key: Configuration (Accessibility, Sharing, iCloud Sync)

Provides fluent interfaces

keychain = Keychain(service: "com.example.github-token")
.label("github.com (kishikawakatsumi)")
.synchronizable(true)
.accessibility(.afterFirstUnlock)
Accessibility Default accessibility matches background application (=kSecAttrAccessibleAfterFirstUnlock)
keychain = Keychain(service: "com.example.github-token")
For background application Creating instance
keychain = Keychain(service: "com.example.github-token")
.accessibility(.afterFirstUnlock)

hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
keychain = Keychain(service: "com.example.github-token")


try keychain
    .accessibility(.afterFirstUnlock)
    .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
tch let error {
print("error: \(error)")

For foreground application Creating instance
keychain = Keychain(service: "com.example.github-token")
.accessibility(.whenUnlocked)

hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
keychain = Keychain(service: "com.example.github-token")


try keychain
    .accessibility(.whenUnlocked)
    .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
tch let error {
print("error: \(error)")

:couple: Sharing Keychain items
keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
:arrows_counterclockwise: Synchronizing Keychain items with iCloud Creating instance
keychain = Keychain(service: "com.example.github-token")
.synchronizable(true)

hain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef"
One-shot
keychain = Keychain(service: "com.example.github-token")


try keychain
    .synchronizable(true)
    .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi")
tch let error {
print("error: \(error)")

:fu: Touch ID integration

Any Operation that require authentication must be run in the background thread.
If you run in the main thread, UI thread will lock for the system to try to display the authentication dialog.

:closed_lock_with_key: Adding a Touch ID protected item

If you want to store the Touch ID protected Keychain item, specify accessibility and authenticationPolicy attributes.

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

atchQueue.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...
}

:closed_lock_with_key: Updating a Touch ID protected item

The same way as when adding.

Do not run in the main thread if there is a possibility that the item you are trying to add already exists, and protected. Because updating protected items requires authentication.

Additionally, you want to show custom authentication prompt message when updating, specify an authenticationPrompt attribute. If the item not protected, the authenticationPrompt parameter just be ignored.

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

atchQueue.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...
}

:closed_lock_with_key: Obtaining a Touch ID protected item

The same way as when you get a normal item. It will be displayed automatically Touch ID or passcode authentication If the item you try to get is protected.
If you want to show custom authentication prompt message, specify an authenticationPrompt attribute. If the item not protected, the authenticationPrompt parameter just be ignored.

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

atchQueue.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...
}

:closed_lock_with_key: Removing a Touch ID protected item

The same way as when you remove a normal item. There is no way to show Touch ID or passcode authentication when removing Keychain items.

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


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

:key: Shared Web Credentials

Shared web credentials is a programming interface that enables native iOS apps to share credentials with their website counterparts. For example, a user may log in to a website in Safari, entering a user name and password, and save those credentials using the iCloud Keychain. Later, the user may run a native app from the same developer, and instead of the app requiring the user to reenter a user name and password, shared web credentials gives it access to the credentials that were entered earlier in Safari. The user can also create new accounts, update passwords, or delete her account from within the app. These changes are then saved and used by Safari.
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/

keychain = Keychain(server: "https://www.kishikawakatsumi.com", protocolType: .HTTPS)

username = "kishikawakatsumi@mac.com"

irst, check the credential in the app's Keychain
et password = try? keychain.get(username) {
// If found password in the Keychain,
// then log into the server
se {
// 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)
    }
}

Request all associated domain's credentials
hain.requestSharedWebCredential { (credentials, error) -> () in


Generate strong random password

Generate strong random password that is in the same format used by Safari autofill (xxx-xxx-xxx-xxx).

password = Keychain.generatePassword() // => Nhu-GKm-s3n-pMx
How to set up Shared Web Credentials
  1. Add a com.apple.developer.associated-domains entitlement to your app. This entitlement must include all the domains with which you want to share credentials.

  2. Add an apple-app-site-association file to your website. This file must include application identifiers for all the apps with which the site wants to share credentials, and it must be properly signed.

  3. When the app is installed, the system downloads and verifies the site association file for each of its associated domains. If the verification is successful, the app is associated with the domain.

More details:
https://developer.apple.com/library/ios/documentation/Security/Reference/SharedWebCredentialsRef/

:key: Debugging
Display all stored items if print keychain object
keychain = Keychain(server: "https://github.com", protocolType: .https)
t("\(keychain)")



uthenticationType: default, key: kishikawakatsumi, server: github.com, class: internetPassword, protocol: https]
uthenticationType: default, key: hirohamada, server: github.com, class: internetPassword, protocol: https]
uthenticationType: default, key: honeylemon, server: github.com, class: internetPassword, protocol: https]

Obtaining all stored keys
keychain = Keychain(server: "https://github.com", protocolType: .https)

keys = keychain.allKeys()
key in keys {
int("key: \(key)")



 kishikawakatsumi
 hirohamada
 honeylemon
Obtaining all stored items
keychain = Keychain(server: "https://github.com", protocolType: .https)

items = keychain.allItems()
item in items {
int("item: \(item)")



: [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]
Requirements

| | OS | Swift | |————|—————————————-|—————| | v1.1.x | iOS 7+, OSX 10.9+ | 1.1 | | v1.2.x | iOS 7+, OSX 10.9+ | 1.2 | | v2.0.x | iOS 7+, OSX 10.9+, watchOS 2+ | 2.0 | | v2.1.x | iOS 7+, OSX 10.9+, watchOS 2+ | 2.0 | | v2.2.x | iOS 8+, OSX 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1 | | v2.3.x | iOS 8+, OSX 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1, 2.2 | | v2.4.x | iOS 8+, OSX 10.9+, watchOS 2+, tvOS 9+ | 2.2, 2.3 | | v3.0.x | iOS 8+, OSX 10.9+, watchOS 2+, tvOS 9+ | 3.x | | v3.1.x | iOS 8+, OSX 10.9+, watchOS 2+, tvOS 9+ | 3.x, 4.0 |

Installation
CocoaPods

KeychainAccess is available through CocoaPods. To install it, simply add the following lines to your Podfile:

frameworks!
'KeychainAccess'
Carthage

KeychainAccess is available through Carthage. To install it, simply add the following line to your Cartfile:

github "kishikawakatsumi/KeychainAccess"

Swift Package Manager

KeychainAccess is also available through Swift Package Manager. First, create Package.swift that its package declaration includes:

rt PackageDescription

package = Package(
dependencies: [
    .Package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", majorVersion: 2)
]

Then, type

ift build
To manually add to your project
  1. Add Lib/KeychainAccess.xcodeproj to your project
  2. Link KeychainAccess.framework with your target
  3. Add Copy Files Build Phase to include the framework to your application bundle

See iOS Example Project as reference.

Author

kishikawa katsumi, kishikawakatsumi@mac.com

License

KeychainAccess is available under the MIT license. See the LICENSE file for more info.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.