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.
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.
Share Improve this question edited Nov 28, 2024 at 10:20 pankaj asked Nov 28, 2024 at 10:06 pankajpankaj 8,39816 gold badges74 silver badges118 bronze badges 1 |1 Answer
Reset to default -1Try 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)
kSecAttrKeyType
. Have you tried removing this from the queries for adding and getting? Note, that the key itself has been created with an attribute kSecAttrKeyType. Possibly it conflicts with your setting in the query and what actually has been set when creating it? – CouchDeveloper Commented Nov 29, 2024 at 12:14