pankaj
pankaj

Reputation: 8378

Unable to fetch key from Keychain in Swift

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

Answers (1)

Rushabh Shah
Rushabh Shah

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

Related Questions