Given a SecKeyRef, how do I return the Key Class Values?

Given a SecKeyRef such as the example below, how do I get the class?

"<SecCDSAKeyRef 0x600000d37030: algorithm id: 1, class=1, algorithm=2a, usage=800001ff attrs=39>",

The possible values kSecAttrKeyClassPublic, kSecAttrKeyClassPrivate and kSecAttrKeyClassSymmetric are documented below, but the function used to retrieve those values seems missing.

https://developer.apple.com/documentation/security/ksecattrkeyclasspublic?language=objc

Replies

Check out SecKeyCopyAttributes.

Having said that, it’s rare that you actually need to do this. Most of the time you know what type of key you’re dealing with because of the way that you got that key. For example, in the context of your other thread, you get the key by searching the keychain for a private key that matches a particular public key hash, and thus you’re certain that you’re working with a private key.

Oh, one more thing: kSecAttrKeyClassSymmetric is only relevant for macOS’s legacy file-based keychains. If you need to store a symmetric key in the data protection keychain, use a generic password item.

If you’re not familiar with the weird way that keychain works on macOS, check out TN3137 On Mac keychain APIs and implementations.

Also, I suspect that you’d benefit from reading through:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The code that needs to do this is the Redwax Tool at https://redwax.eu/rt/, which does universal certificate and key handling between different systems.

SecKeyCopyAttributes() doesn't work in this case, because when the key you are trying to find the class of is a private key, you have to unlock the keychain for this key before keychain will tell you this is a private key, and the whole point is to not unlock private keys that are unrelated to the task - a chicken and egg problem. This gives a terrible user experience as the user is asked to unlock every key one by one.

One possible way around this is if there was a search parameter to SecItemCopyMatching() that allows you to restrict the class to kSecAttrKeyClassPublic, but this too appears not possible / undocumented.

What I'm looking for are the public keys that keychain has, so I can match them up with certs on the outside, which will then trigger an attempt to unlock the private keys that are relevant and no others.

There is a search parameter to SecItemCopyMatching() - pass value of kSecAttrKeyClassPublic into the key kSecAttrKeyClass and you get public keys.

    CFStringRef dictkeys[] = {
        kSecClass,
        kSecMatchLimit,
        kSecAttrKeyClass,
        kSecReturnRef,
    };

    CFTypeRef dictvalues[] = {
        kSecClassKey,
        kSecMatchLimitAll,
        kSecAttrKeyClassPublic,
        kCFBooleanTrue,
    };

    CFDictionaryRef query = CFDictionaryCreate(
        NULL,
        (const void **) dictkeys,
        dictvalues,
        sizeof(dictkeys) / sizeof(dictkeys[0]),
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks
    );

    OSStatus err = SecItemCopyMatching(query, &keys);

One possible way around this is if there was a search parameter to SecItemCopyMatching that allows you to restrict the class to kSecAttrKeyClassPublic, but this too appears not possible

That’s definitely possible, and the code in your follow-up post is my recommended way of doing it (modulo the CF silliness, which I try to avoid).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"