0

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.

1
  • In your use case, you don't need to specify the attribute 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? Commented Nov 29, 2024 at 12:14

1 Answer 1

-1

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)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for answering Rushabh, will it work if I remove valueData? I just want to save the key and no data
Yes, it works. Please check with your updated Query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.