14

I've looked over the Security framework documentation but I can't seem to be able to find a way to get all of the certificates on a given keychain. Are there methods to accomplish this?

2 Answers 2

17

After mining the documentation, header files, and source files, I’ve come up with the following code:

#import <Security/Security.h> - (void)logMessageForStatus:(OSStatus)status functionName:(NSString *)functionName { CFStringRef errorMessage; errorMessage = SecCopyErrorMessageString(status, NULL); NSLog(@"error after %@: %@", functionName, (NSString *)errorMessage); CFRelease(errorMessage); } - (void)listCertificates { OSStatus status; SecKeychainSearchRef search = NULL; // The first argument being NULL indicates the user's current keychain list status = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &search); if (status != errSecSuccess) { [self logMessageForStatus:status functionName:@"SecKeychainSearchCreateFromAttributes()"]; return; } SecKeychainItemRef searchItem = NULL; while (SecKeychainSearchCopyNext(search, &searchItem) != errSecItemNotFound) { SecKeychainAttributeList attrList; CSSM_DATA certData; attrList.count = 0; attrList.attr = NULL; status = SecKeychainItemCopyContent(searchItem, NULL, &attrList, (UInt32 *)(&certData.Length), (void **)(&certData.Data)); if (status != errSecSuccess) { [self logMessageForStatus:status functionName:@"SecKeychainItemCopyContent()"]; CFRelease(searchItem); continue; } // At this point you should have a valid CSSM_DATA structure // representing the certificate SecCertificateRef certificate; status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &certificate); if (status != errSecSuccess) { [self logMessageForStatus:status functionName:@"SecCertificateCreateFromData()"]; SecKeychainItemFreeContent(&attrList, certData.Data); CFRelease(searchItem); continue; } // Do whatever you want to do with the certificate // For instance, print its common name (if there's one) CFStringRef commonName = NULL; SecCertificateCopyCommonName(certificate, &commonName); NSLog(@"common name = %@", (NSString *)commonName); if (commonName) CFRelease(commonName); SecKeychainItemFreeContent(&attrList, certData.Data); CFRelease(searchItem); } CFRelease(search); } 
Sign up to request clarification or add additional context in comments.

5 Comments

Absolutely brilliant. Thank you so much!
@Dylan Cheers. Consider filing an enhancement request radar (or three, actually) with Apple: this could be sample code, this could be in the Security Programming Guide, and they could provide a simpler, Cocoa-based API for this.
The searchItem returned by SecKeychainSearchCopyNext is already a SecCertificateRef. (You are looking for items in the certificate class, so every result is a certificate item.) It isn't necessary to extract and reinterpret its data: a simple typecast will get better results.
will it work for iOS? NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: (id)kCFBooleanTrue, kSecReturnRef, (id)kSecMatchLimitOne, kSecMatchLimit, ( id)kSecClassCertificate, kSecClass, nil]; CFTypeRef result = NULL; SecItemCopyMatching(( CFDictionaryRef)query, &result); NSLog(@"%@", ( id)result); returns me null.
Any possible solution for iOS devices?
16

If you target Mac OS 10.6 or later, you can use SecItemCopyMatching to easily query the keychain:

SecKeychainRef keychain = ... NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: kSecClassCertificate, kSecClass, [NSArray arrayWithObject:(id)keychain], kSecMatchSearchList, kCFBooleanTrue, kSecReturnRef, kSecMatchLimitAll, kSecMatchLimit, nil]; NSArray *items = nil; OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&items); if (status) { if (status != errSecItemNotFound) LKKCReportError(status, @"Can't search keychain"); return nil; } return [items autorelease]; // items contains all SecCertificateRefs in keychain 

Note that you must not use this to implement certificate validation — the presence of a CA certificate in a keychain does not indicate that it is trusted to sign certificates for any particular policy. See the Certificate, Key, and Trust Programming Guide to learn how to do certificate validation with the Keychain.

http://developer.apple.com/library/mac/#documentation/Security/Conceptual/CertKeyTrustProgGuide/03tasks/tasks.html#//apple_ref/doc/uid/TP40001358-CH205-SW1

1 Comment

This is a great answer, since SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext are deprecated as of 10.7. This does require some crazy casts to work with ARC, but using a CFMutableDictionaryRef and setting these keys works just as well. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.