KeychainStorage

public class KeychainStorage : DataStoring

KeychainStorage


The Keychain Storage component provides secure storage of Data types. See Keychain Service for more information about Keychain.

Note: This component is only using encryption mechanisms which are available in iOS.

Usage

Store Managing

The createStore, openStore and removeStore are class functions. The createStore and openStore functions return a KeychainStorage instance. The store instance can use the instance level functions to manipulate the store. KeychainStorage can use a Ciphering protocol complient class which performs the encryption/decryption of the stored data. The default implementation of the Ciphering protocol is the Cipher class. By default the KeychainStorage doesn’t use encyption so you have to pass an implementation of the Ciphering protocol to achieve encryption.

Store Creation

The store fails to create if the name is not UTF-8 encoded string or the store already exists. After creation the store is already opened. If the store is not shared the accessGroup parameter can be nil.

do {
    let cipher = try Cipher(password: <#password#>, salt: <#salt#>)
    let keychainStorage = try KeychainStorage.createStore(name: <#storeName#>, cipher: cipher, accessGroup: <#accessGroup#>)
} catch  {
    logger.error("An error occurred while creating the store.", error: error)
}

Opening Store

You must open the store before you can interact with it. The store fails to open if the store doesn’t exists. If the store is not shared the accessGroup parameter can be nil.

do {
    let keychainStorage = try KeychainStorage.openStore(name: <#storeName#>, cipher: cipher, accessGroup: <#accessGroup#>)
} catch  {
    logger.error("An error occurred while opening the store.", error: error)
}

Removing Store

Removing Data from the store can throw errors if the name is not UTF-8 encoded string or could not remove its stored data. If the store is not shared the accessGroup parameter can be nil.

do {
    try KeychainStorage.removeStore(name: <#storeName#>, accessGroup: <#accessGroup#>)
} catch {
    logger.error("An error occurred while removing the store", error: error)
}

Changing Cipher on Store

Changing the Cipher on the store will encrypt all data with the new Cipher. The change cipher can throw errors if the data can’t encrypt with the new Cipher.

do {
    let cipher = try Cipher(password: <#password#>, salt: <#salt#>)
    let keychainStorage = try KeychainStorage.createStore(name: <#storeName#>, cipher: cipher, accessGroup: <#accessGroup#>)

    let newCipher = try Cipher(password: <#newPassword#>, salt: <#newSalt#>)

    try keychainStorage.changeCipher(to: newCipher)
} catch  {
    logger.error("An error occurred while changing the cipher.", error: error)
}

Access Group and App Group

The store sharing is based on the Keychain Access Group. The Keychain Access Group can be used via the App Groups.

The App Groups should be configured in the capabilities tab of the target in the Xcode. See Configure app groups for more information about configuration of App Groups. The configured App Group should be the same in all app where the content should be shared.

The App Group is the expected string in the KeychainStorage API where Access Group is necessary.

Manipulating Store

A KeychainStorage has many methods to set and retrieve different data.

Reading from Store

The data will be decrypted with the Cipher of the store instance after reading from the Keychain. Reading from store returns a Data object if successful, nil if the key doesn’t exist and throws an error upon failure.

do {
    let data = try keychainStorage.data(for: key)
} catch {
    logger.error("An error occurred while reading from the store.", error: error)
}

Putting into Store

The data will be encrypted with the Cipher of the store instance. Putting into store throws an error upon failure.

do {
    try keychainStorage.put(data: data, for: key)
} catch {
    logger.error("An error occurred while putting into the store.", error: error)
}

Removing from Store

Removing data from the store throws an error upon failure.

do {
    try keychainStorage.removeData(for: key)
} catch {
    logger.error("An error occurred while removing from the store.", error: error)
}

Removing all data from Store

Removing all existing data from the store throws an error upon failure.

do {
    try keychainStorage.removeAllData()
} catch {
    logger.error("An error occurred while removing all data from the store.", error: error)
}

Keys of the Store

Collect all keys of the stored data. Returns a Set of Strings containing the existing keys in the store

do {
    let keys = try keychainStorage.keys()
} catch {
    logger.error("An error occurred while get all keys from the store.", error: error)
}

Thread Safety

All instance methods are safe to use from multiple threads.

DataStoring

The KeychainStorage implements the DataStoring protocol. It provides methods to open the store, as well as for changing the encryption method with a new Cipher instance. Without Cipher the data in the store will not be encrypted.

KeychainStorage component Logger ID

This component uses the following name prefix for logging: ‘SAP.Foundation.KeychainStorage’

Data storage using the Keychain

  • UTF-8 encoded name of the store

    Declaration

    Swift

    public let name: String
  • Ciphering implementation to encrypt/decrypt the data. To set this data use the changeCipher(to: Ciphering) function.

    Declaration

    Swift

    public var cipher: Ciphering { get }
  • Share Keychain items between apps by using Keychain Groups with this name

    Declaration

    Swift

    public let accessGroup: String
  • Creates a KeychainStorage instance

    Throws

    KeychainStorageError

    Declaration

    Swift

    public class func createStore(name: String, cipher: Ciphering? = nil, accessGroup: String = "") throws -> KeychainStorage

    Parameters

    name

    UTF-8 encoded name of the store

    cipher

    Ciphering implementation to encrypt/decrypt the data

    accessGroup

    share Keychain items between apps by using Keychain Groups with this name. To create shared store use a non empty String created with the accessGroup(from keychainGroup: String) function.

    Return Value

    KeychainStorage instance

  • Opens a KeychainStorage

    Throws

    KeychainStorageError

    Declaration

    Swift

    public class func openStore(name: String, cipher: Ciphering? = nil, accessGroup: String = "") throws -> KeychainStorage

    Parameters

    name

    UTF-8 encoded name of the store

    cipher

    Ciphering implementation to encrypt/decrypt the data

    accessGroup

    share Keychain items between apps by using Keychain Groups with this name. To create shared store use a non empty String created with the accessGroup(from keychainGroup: String) function.

    Return Value

    KeychainStorage instance

  • Checks if a KeychainStorage exists under the specified name and accessGroup.

    Throws

    KeychainStorageError

    Declaration

    Swift

    public class func storeExists(name: String, accessGroup: String = "") throws -> Bool

    Parameters

    name

    UTF-8 encoded name of the store

    accessGroup

    share Keychain items between apps by using Keychain Groups with this name. To create shared store use a non empty String created with the accessGroup(from keychainGroup: String) function.

    Return Value

    true if the store exists, false otherwise

  • Removes the given KeychainStorage

    Throws

    KeychainStorageError

    Declaration

    Swift

    public class func removeStore(name: String, accessGroup: String = "") throws

    Parameters

    store

    KeychainStorage instance

  • Store an item for the given key

    Throws

    KeychainStorageError

    Declaration

    Swift

    public func put(data: Data, for key: String) throws

    Parameters

    data

    data to store

    key

    key for the data

  • Finds the item connected to the given key

    Throws

    KeychainStorageError

    Declaration

    Swift

    public func data(for key: String) throws -> Data?

    Parameters

    key

    key of the searched data

    Return Value

    result data if found, nil otherwise

  • Removes the item connected to the given key

    Throws

    KeychainStorageError

    Declaration

    Swift

    public func removeData(for key: String) throws

    Parameters

    key

    key of the removed data

  • Removes all items of the store

    Declaration

    Swift

    public func removeAllData() throws
  • Collect all keys of the stored values

    Throws

    KeychainStorageError

    Declaration

    Swift

    public func keys() throws -> Set<String>

    Return Value

    set of String with the keys

  • Change the cipher on the store instance. This should call typically on the passcode change. All the values in the store will be encrypted with the new Ciphering.

    Throws

    KeychainStorageError

    Declaration

    Swift

    public func changeCipher(to newCipher: Ciphering) throws

    Parameters

    newCipher

    the new cipher instance