Get Certificates in Keychain

2020-02-08 06:21发布

问题:

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?

回答1:

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);
}


回答2:

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