iosswiftsecuritykeychainios13

How to obtain a digital identity from the keychain?


I was following the tutorial by Apple: https://developer.apple.com/documentation/security/certificate_key_and_trust_services/certificates/storing_a_certificate_in_the_keychain

and

https://developer.apple.com/documentation/security/certificate_key_and_trust_services/identities/storing_an_identity_in_the_keychain

on how to store and retrieve an identity from the keychain.

Storing the identity seems to work fine, errSecSuccess is returned. However, when trying to obtain any identity, I get back errSecItemNotFound (or -25300).

Even dumping the keychain does not work as expected; after successfully adding an identity, SecItemCopyMatching still returns errSecItemNotFound.

See the following piece of code that pretty much matches the extract provided in the documentation:

// Store the digital identity in the keychain:
let addQuery: [String : Any] = [kSecClass as String: kSecClassIdentity,
                                kSecValueRef as String: identity,
                                kSecAttrLabel as String: "myid"]

guard SecItemAdd(addQuery as CFDictionary, nil) == errSecSuccess else {
    print("Could not store identity in keychain")
    return false
}

// Obtain the digital identity from the keychain:
let getQuery: [String : Any] = [kSecClass as String: kSecClassIdentity,
                                kSecAttrLabel as String: "myid",
                                kSecReturnRef as String: kCFBooleanTrue!]
var item: CFTypeRef?
let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
guard status == errSecSuccess else {
    print("Could not find identity in keychain, OSerror: \(status)") // <---- -25300
    return false
}

let localIdentity = item as! SecIdentity

// [...]

For the sake of completion, this is how I "dump" the keychain (at least the identities):

let getQuery: [String : Any] = [kSecClass as String : secClass,
                                kSecReturnData as String  : kCFBooleanTrue!,
                                kSecReturnAttributes as String : kCFBooleanTrue!,
                                kSecReturnRef as String : kCFBooleanTrue!,
                                kSecMatchLimit as String : kSecMatchLimitAll]
var items: CFTypeRef?
let status = SecItemCopyMatching(getQuery as CFDictionary, &items) // <--- again, status = -25300

What's missing?

I went through the docs multiple times. Does storing items in the keychain take some time? If so, does the example provided by Apple mention that?

Why do I get an errSecSuccess when storing the item but it won't let me read the item right after?

I‘m also testing on a real device.


Solution

  • I finally found the answer myself.

    Using labels on an identity is tricky because identities are not stored in the keychain as an atomic item but are store as a separate private key and certificate, and those items use labels in different ways. 

    https://forums.developer.apple.com/thread/98029

    That is:

    1. Pass kSecReturnPersistentRef to SecItemAdd when you add the identity to the keychain
    2. Save the persistent reference wherever
    3. Later on, when you need the identity back, call SecItemCopyMatching with that persistent reference

    This makes sense and I was finally able to solve my problem this way.