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
-
Share Keychain items between apps by using Keychain Groups with this name
Declaration
Swift
public let accessGroup: String
-
Creates a
KeychainStorage
instanceThrows
KeychainStorageErrorDeclaration
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 dataaccessGroup
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
KeychainStorageErrorDeclaration
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 dataaccessGroup
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
KeychainStorageErrorDeclaration
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
KeychainStorageErrorDeclaration
Swift
public class func removeStore(name: String, accessGroup: String = "") throws
Parameters
store
KeychainStorage
instance
-
Store an item for the given key
Throws
KeychainStorageErrorDeclaration
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
KeychainStorageErrorDeclaration
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
KeychainStorageErrorDeclaration
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
KeychainStorageErrorDeclaration
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
KeychainStorageErrorDeclaration
Swift
public func changeCipher(to newCipher: Ciphering) throws
Parameters
newCipher
the new cipher instance