Reputation: 8378
I am saving a private key to keychain and now want to retrieve it but some how not able to do it. Following is my code:
public func storeKeyToKeychain(_ key: SecKey, tag: String) -> Bool {
let deleteQuery: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: tag.data(using: .utf8)!
]
SecItemDelete(deleteQuery as CFDictionary) // Clean existing key before adding
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: tag.data(using: .utf8)!,
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecValueRef as String: key,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
let status = SecItemAdd(query as CFDictionary, nil)
if status != errSecSuccess {
print("Key storage failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))")
} else {
print("Key stored successfully.")
}
return status == errSecSuccess
}
public func getKeyFromKeychain(tag: String) -> SecKey? {
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: tag.data(using: .utf8)!, // Ensure tag is Data
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecReturnRef as String: true // Retrieve the SecKey reference
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
if status == errSecSuccess, let key = item as! SecKey? {
print("Successfully retrieved SecKey.")
return key
} else if status == errSecItemNotFound {
print("Key not found in the Keychain.")
} else {
print("Keychain retrieval failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))")
}
return nil
}
I can see the Key stored successfully message. So key is saved but when I try to fetch it, I getting nil in item which means I am not able to fetch it. I have checked in logs that errSecSuccess is true while fetching it but still I am unable to get the key in item.
Upvotes: 0
Views: 55
Reputation: 398
Try with this code. I am using this one and it works fine.
class KeychainAccess {
func addKeychainData(itemKey: String, itemValue: String) throws {
guard let valueData = itemValue.data(using: .utf8) else {
print("Keychain: Unable to store data, invalid input - key: \(itemKey), value: \(itemValue)")
return
}
//delete old value if stored first
do {
try deleteKeychainData(itemKey: itemKey)
} catch {
print("Keychain: nothing to delete...")
}
let queryAdd: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject,
kSecValueData as String: valueData as AnyObject,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
let resultCode: OSStatus = SecItemAdd(queryAdd as CFDictionary, nil)
if resultCode != 0 {
print("Keychain: value not added - Error: \(resultCode)")
} else {
print("Keychain: value added successfully")
}
}
func deleteKeychainData(itemKey: String) throws {
let queryDelete: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject
]
let resultCodeDelete = SecItemDelete(queryDelete as CFDictionary)
if resultCodeDelete != 0 {
print("Keychain: unable to delete from keychain: \(resultCodeDelete)")
} else {
print("Keychain: successfully deleted item")
}
}
func queryKeychainData (itemKey: String) throws -> String? {
let queryLoad: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject,
kSecReturnData as String: kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
if resultCodeLoad != 0 {
print("Keychain: unable to load data - \(resultCodeLoad)")
return nil
}
guard let resultVal = result as? NSData, let keyValue = NSString(data: resultVal as Data, encoding: String.Encoding.utf8.rawValue) as String? else {
print("Keychain: error parsing keychain result - \(resultCodeLoad)")
return nil
}
return keyValue
}
}
Create one object and use that object to store and fetch.
let keychain = KeychainAccess()
do {
try? self.keychain.addKeychainData(itemKey: "KeyChainUserName", itemValue: result?.customer?.email ?? "")
} catch {
print("error of user Name")
}
do {
try? self.keychain.addKeychainData(itemKey: "KeyChainPassword", itemValue: "\(result?.customer?.id ?? 0)" )
} catch {
print("error of password")
}
Fetch data from the Keychain.
let userName = try? self.keychain.queryKeychainData(itemKey: "KeyChainUserName")
print("UserName:", userNam)
Upvotes: -1