Thanks so much. I wrote the below two methods to create and read keys.
Another small question - is it possible to store/get the specific creation time of the key generated when reading the key from the keychain?
internal func generateAndStoreSymmetricKey(withKeychainTag: String) throws {
// Parameter:
let alias = withKeychainTag
let key = SymmetricKey(size: .bits256)
let addQuery:[CFString:Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrLabel: alias,
kSecAttrAccount: "Account \(alias)",
kSecAttrService: "Service \(alias)",
kSecReturnAttributes: true,
kSecValueData: key.rawRepresentation
]
var result: CFTypeRef?
let status = SecItemAdd(addQuery as CFDictionary, &result)
guard status == errSecSuccess else {
throw Error.client("Failed to insert symmetric key into keychain: \(withKeychainTag)")
}
}
internal func retrieveSymmetricKey(withKeychainTag: String) throws -> SymmetricKey? {
// Parameter:
let alias = withKeychainTag
// Seek a generic password with the given account.
let query = [kSecClass: kSecClassGenericPassword,
kSecAttrAccount: "Account \(alias)",
kSecUseDataProtectionKeychain: true,
kSecReturnData: true] as [String: Any]
// Find and cast the result as data.
var item: CFTypeRef?
switch SecItemCopyMatching(query as CFDictionary, &item) {
case errSecSuccess:
guard let data = item as? Data else { throw Error.client("Fail to convert the key reference to Data.") }
return try SymmetricKey(rawRepresentation: data) // Convert back to a key.
case errSecItemNotFound: return nil
default: throw Error("Error in reading the key")
}
}