Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SecItemCopyMatching - Finding specific kSecClassIdentity by name

Tags:

macos

keychain

I'm trying to find a specific entry in my Mac OS X keychain, based on it's name (kSecAttrLabel), but it looks like SecItemCopyMatching is broken and applies no filtering whatsoever when looking for items of type: kSecClassIdentity.

This piece of code will return all identities found in all keychains, despite the kSecAttrLabel: @"MyIdentity" parameter:

NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge NSString*)kSecClassIdentity,
                     (__bridge id)kSecAttrLabel: @"MyIdentity",
                     (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
                     (__bridge id)kSecReturnAttributes: @YES,
                     (__bridge id)kSecReturnRef: @YES };

OSStatus status;
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);

Granted, I can then find the one identity I'm looking for by filtering manually the returned array, however, beside the fact that, IMHO, it should just work, I also would like to remove this identity from my keychain using SecItemDelete(), which takes a query as parameter, just like SecItemCopyMatching. If filtering doesn't work for SecItemCopyMatching, then it is likely that it won't work for SecItemDelete and this mean I will simply erase the content of my keychain if I try to call SecItemDelete with this query.

What am I doing wrong?

like image 940
Guy Moreillon Avatar asked Feb 22 '26 19:02

Guy Moreillon


1 Answers

I think I have just found a solution. It was suggested on on another forum that

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.

This made me realise that a solution is to search for a certificate by label using SecItemCopyMatching, and then create the identity using SecIdentityCreateWithCertificate. The latter call should find the matching private key in the Keychain. Here is the full code (in C++) that seems to work for me on macOS Mojave:

SecIdentityRef identity = nullptr;
const char* certificateName = ...;
const void* keys[] = {
    kSecClass,
    kSecMatchLimit,
    kSecReturnRef,
    kSecAttrLabel
};
const void* values[] = {
    kSecClassCertificate,
    kSecMatchLimitOne,
    kCFBooleanTrue,
    CFStringCreateWithCString(nullptr, certificateName, kCFStringEncodingUTF8)
};
CFDictionaryRef query = CFDictionaryCreate(nullptr, keys, values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFTypeRef result;
OSStatus status = SecItemCopyMatching(query, &result);
CFRelease(query);
CFRelease(values[3]);
if (status) {
    // error
}
else {
    SecCertificateRef certificate = (SecCertificateRef)result;
    status = SecIdentityCreateWithCertificate(nullptr, certificate, &identity);
    CFRelease(certificate);
    if (status) {
        // error
    }
}
like image 130
Boris Avatar answered Feb 24 '26 08:02

Boris



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!