2011-04-24 142 views

回答

13

挖掘的文档,头文件和源文件后,我想出了下面的代码:

#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); 
} 
+1

绝对的辉煌。非常感谢! – 2011-04-24 16:05:20

+0

@Dylan干杯。考虑向苹果提交一个改进请求雷达(实际上是三个):这可能是示例代码,这可能在安全编程指南中,它们可以为此提供更简单的基于Cocoa的API。 – 2011-04-24 22:45:38

+1

'SecKeychainSearchCopyNext'返回的'searchItem'已经是'SecCertificateRef'。 (您正在查找证书类中的项目,因此每个结果都是一个证书项目。)无需提取并重新解释其数据:简单的类型转换将获得更好的结果。 – 2011-10-21 18:50:17

13

如果目的MAC OS 10.6或更高版本,可以使用SecItemCopyMatching轻松查询钥匙扣:

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 

请注意,你不能使用这个实现证书验证 - 在钥匙链CA证书的存在并不表示它是信任签署证书的任何特定的政策。请参阅证书,密钥和信任编程指南以了解如何使用钥匙串进行证书验证。

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

+0

这是一个很好的答案,因为'SecKeychainSearchCreateFromAttributes'和'SecKeychainSearchCopyNext'从10.7开始被弃用。这确实需要一些疯狂的强制转换才能使用ARC,但使用'CFMutableDictionaryRef'并设置这些键也同样适用。谢谢! – 2013-02-09 07:14:43