2012-01-13 482 views
25

我有一個證書(SecCertificateRef),我可以檢查它是否有效,並且我可以使用SecCertificateCopySubjectSummary提取「摘要」。SecCertificateRef:如何獲取證書信息?

究竟是什麼「總結」?我不理解術語「包含證書內容的可讀摘要的字符串」。在Apple文檔中。我認爲,他們是指證書中的「CN」,對嗎?

是否有任何方法從SecCertificateRef中獲取清晰的X509信息?轉換爲鑰匙串對象的幫助?

我想要這樣的東西,我特別關注「CN」,將它與我提交的URL進行比較以避免中間人攻擊。 (或任何更好的想法?)

這是我想擁有的一切:

Version: 3 (0x2) 
     Serial Number: 1 (0x1) 
     Signature Algorithm: md5WithRSAEncryption 
     Issuer: C=XY, ST=Austria, L=Graz, O=TrustMe Ltd, OU=Certificate Authority, CN=CA/[email protected] 
     Validity 
      Not Before: Oct 29 17:39:10 2000 GMT 
      Not After : Oct 29 17:39:10 2001 GMT 
     Subject: C=DE, ST=Austria, L=Vienna, O=Home, OU=Web Lab, CN=anywhere.com/[email protected] 
     Subject Public Key Info: 
      Public Key Algorithm: rsaEncryption 
      RSA Public Key: (1024 bit) 
       Modulus (1024 bit): 
        00:c4:40:4c:6e:14:1b:61:36:84:24:b2:61:c0:b5: 
        d7:e4:7a:a5:4b:94:ef:d9:5e:43:7f:c1:64:80:fd: 
        9f:50:41:6b:70:73:80:48:90:f3:58:bf:f0:4c:b9: 
        90:32:81:59:18:16:3f:19:f4:5f:11:68:36:85:f6: 
        1c:a9:af:fa:a9:a8:7b:44:85:79:b5:f1:20:d3:25: 
        7d:1c:de:68:15:0c:b6:bc:59:46:0a:d8:99:4e:07: 
        50:0a:5d:83:61:d4:db:c9:7d:c3:2e:eb:0a:8f:62: 
        8f:7e:00:e1:37:67:3f:36:d5:04:38:44:44:77:e9: 
        f0:b4:95:f5:f9:34:9f:f8:43 
       Exponent: 65537 (0x10001) 
     X509v3 extensions: 
      X509v3 Subject Alternative Name: 
       email:[email protected] 
      Netscape Comment: 
       mod_ssl generated test server certificate 
      Netscape Cert Type: 
       SSL Server 
    Signature Algorithm: md5WithRSAEncryption 
     12:ed:f7:b3:5e:a0:93:3f:a0:1d:60:cb:47:19:7d:15:59:9b: 
     3b:2c:a8:a3:6a:03:43:d0:85:d3:86:86:2f:e3:aa:79:39:e7: 
     82:20:ed:f4:11:85:a3:41:5e:5c:8d:36:a2:71:b6:6a:08:f9: 
     cc:1e:da:c4:78:05:75:8f:9b:10:f0:15:f0:9e:67:a0:4e:a1: 
     4d:3f:16:4c:9b:19:56:6a:f2:af:89:54:52:4a:06:34:42:0d: 
     d5:40:25:6b:b0:c0:a2:03:18:cd:d1:07:20:b6:e5:c5:1e:21: 
     44:e7:c5:09:d2:d5:94:9d:6c:13:07:2f:3b:7c:4c:64:90:bf: 
     ff:8e 

回答

23

我不能等待賞金的答案,所以我自己找到了解決方案。正如有人說,Security.framework不給你一種方式來獲得這些信息,所以你需要問的OpenSSL解析證書數據爲您提供:

#import <openssl/x509.h> 

// ... 

NSData *certificateData = (NSData *) SecCertificateCopyData(certificate); 

const unsigned char *certificateDataBytes = (const unsigned char *)[certificateData bytes]; 
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]); 

NSString *issuer = CertificateGetIssuerName(certificateX509); 
NSDate *expiryDate = CertificateGetExpiryDate(certificateX509); 

CertificateGetIssuerNameCertificateGetExpiryDate如下:

static NSString * CertificateGetIssuerName(X509 *certificateX509) 
{ 
    NSString *issuer = nil; 
    if (certificateX509 != NULL) { 
     X509_NAME *issuerX509Name = X509_get_issuer_name(certificateX509); 

     if (issuerX509Name != NULL) { 
      int nid = OBJ_txt2nid("O"); // organization 
      int index = X509_NAME_get_index_by_NID(issuerX509Name, nid, -1); 

      X509_NAME_ENTRY *issuerNameEntry = X509_NAME_get_entry(issuerX509Name, index); 

      if (issuerNameEntry) { 
       ASN1_STRING *issuerNameASN1 = X509_NAME_ENTRY_get_data(issuerNameEntry); 

       if (issuerNameASN1 != NULL) { 
        unsigned char *issuerName = ASN1_STRING_data(issuerNameASN1); 
        issuer = [NSString stringWithUTF8String:(char *)issuerName]; 
       } 
      } 
     } 
    } 

    return issuer; 
} 

static NSDate *CertificateGetExpiryDate(X509 *certificateX509) 
{ 
    NSDate *expiryDate = nil; 

    if (certificateX509 != NULL) { 
     ASN1_TIME *certificateExpiryASN1 = X509_get_notAfter(certificateX509); 
     if (certificateExpiryASN1 != NULL) { 
      ASN1_GENERALIZEDTIME *certificateExpiryASN1Generalized = ASN1_TIME_to_generalizedtime(certificateExpiryASN1, NULL); 
      if (certificateExpiryASN1Generalized != NULL) { 
       unsigned char *certificateExpiryData = ASN1_STRING_data(certificateExpiryASN1Generalized); 

       // ASN1 generalized times look like this: "20131114230046Z" 
       //        format: YYYYMMDDHHMMSS 
       //        indices:
       //             1111 
       // There are other formats (e.g. specifying partial seconds or 
       // time zones) but this is good enough for our purposes since 
       // we only use the date and not the time. 
       // 
       // (Source: http://www.obj-sys.com/asn1tutorial/node14.html) 

       NSString *expiryTimeStr = [NSString stringWithUTF8String:(char *)certificateExpiryData]; 
       NSDateComponents *expiryDateComponents = [[NSDateComponents alloc] init]; 

       expiryDateComponents.year = [[expiryTimeStr substringWithRange:NSMakeRange(0, 4)] intValue]; 
       expiryDateComponents.month = [[expiryTimeStr substringWithRange:NSMakeRange(4, 2)] intValue]; 
       expiryDateComponents.day = [[expiryTimeStr substringWithRange:NSMakeRange(6, 2)] intValue]; 
       expiryDateComponents.hour = [[expiryTimeStr substringWithRange:NSMakeRange(8, 2)] intValue]; 
       expiryDateComponents.minute = [[expiryTimeStr substringWithRange:NSMakeRange(10, 2)] intValue]; 
       expiryDateComponents.second = [[expiryTimeStr substringWithRange:NSMakeRange(12, 2)] intValue]; 

       NSCalendar *calendar = [NSCalendar currentCalendar]; 
       expiryDate = [calendar dateFromComponents:expiryDateComponents]; 

       [expiryDateComponents release]; 
      } 
     } 
    } 

    return expiryDate; 
} 

我實際上只需要發行者的組織名稱和到期日用於我的目的,所以這是我已經包含在下面的所有代碼。但是,基於此,您應該能夠通過閱讀x509.h頭文件來找出其餘的問題。

編輯:

這裏是如何拿到證書。我還沒有提出任何錯誤處理等,例如,您需要檢查trustResulterr等。

NSURLAuthenticationChallenge *challenge; 
SecTrustResultType trustResult; 
SecTrustRef trust = challenge.protectionSpace.serverTrust; 
OSStatus err = SecTrustEvaluate(trust, &trustResult); 
SecCertificateRef certificate = SecGetLeafCertificate(trust); // See Apple docs for implementation of SecGetLeafCertificate 
+0

你能否澄清一下你是如何得到創建X509對象所需的NSData的?什麼是「證書」? NSData * certificateData =(NSData *)SecCertificateCopyData(certificate); – Maggie 2012-02-20 15:33:27

+0

哦,你可以從挑戰中得到如下結果: NSURLAuthenticationChallenge *挑戰; SecTrustResultType trustResult; SecTrustRef trust = challenge.protectionSpace.serverTrust; OSStatus err = SecTrustEvaluate(trust,&trustResult); SecCertiicateRef certificate = SecGetLeafCertificate(trust); //請參閱Apple文檔以獲取SecGetLeafCertificate的實現 – 2012-02-29 14:42:43

+0

呃,這很醜陋。讓我把它放在原來的答案。 – 2012-02-29 14:44:49

2

我不相信這是一個公共API來做到這一點iOS上。在OSX上,有多個SecCertificate API來挑選X.509信息。

0

更好的是,只需使用SecCertificateCopyCommonName即可將CN與您所需的主機名進行比較。

+1

這就是OSX,問題在於詢問iOS。 – 2012-01-17 05:09:56

+0

你是對的,沒有詳細檢查它。 – Allen 2012-01-17 05:17:30

+1

當然,在一個非常有限的意義上。 SecCertificate.h在iOS上提供了大約4種方法。 – 2012-01-17 05:19:39

2

你是對邁克爾的iOS不會給你的API做一個X.509證書一工作。幸運的是,授予您訪問實際(ASN.1)編碼的證書數據。從那裏你可以做你自己的解碼(沒有多少樂趣),或者將它委託給現有的庫,就像你使用OpenSSL一樣。

這是我使用.NET框架的版本。這意味着MonoTouch開發人員(以及MonoMac開發人員)需要在其應用程序中與SecCertificateRef進行互操作。

public void Show (SecCertificate sc) 
{ 
    // get the SecCertificate "raw", i.e. ASN.1 encoded, data 
    byte[] data = sc.DerData.ToArray<byte>(); 
    // the build the managed X509Certificate2 from it 
    X509Certificate2 cer = new X509Certificate2 (data); 
    // to get all properties/methods available in .NET (pretty exhaustive) 
    Console.WriteLine ("SubjectName: {0}", cer.Subject); 
    Console.WriteLine ("IssuerName: {0}", cer.Issuer); 
    Console.WriteLine ("NotBefore: {0}", cer.NotBefore); 
    Console.WriteLine ("NotAfter: {0}", cer.NotAfter); 
    Console.WriteLine ("SerialNumber: {0}", cer.SerialNumber); 
    // ... 
} 
+1

(快樂的嘆息......)用C#開發iOS是不是很可愛! 我在2014年12月嘗試了Xamarin,但儘管使用C#非常棒,但我發現Xamarin Studio for Mac太不穩定,無法推薦給我的公司。看着這段代碼,我開始懷念它... – 2015-01-28 14:00:44

1

如果你想做到這一點沒有OpenSSL的一個可以使用蘋果提取鍵某種原因。第一個將提取(僅)主體和發行者(對於大多數其他事物,例如到期日期,還有更多kSecOIDX509),並通過它們進行打印。

 +(NSString*)stringFromCerificateWithLongwindedDescription:(SecCertificateRef) certificateRef { 
    if (certificateRef == NULL) 
     return @""; 

    CFStringRef commonNameRef; 
    OSStatus status; 
    if ((status=SecCertificateCopyCommonName(certificateRef, &commonNameRef)) != errSecSuccess) { 
     NSLog(@"Could not extract name from cert: %@", 
       SecCopyErrorMessageString(status, NULL)); 
     return @"Unreadable cert";    
    }; 

    CFStringRef summaryRef = SecCertificateCopySubjectSummary(certificateRef); 
    if (summaryRef == NULL) 
     summaryRef = CFRetain(commonNameRef); 

    CFErrorRef error; 

    const void *keys[] = { kSecOIDX509V1SubjectName, kSecOIDX509V1IssuerName }; 
    const void *labels[] = { "Subject", "Issuer" }; 
    CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks); 

    CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection,&error); 
    NSMutableString *longDesc = [[NSMutableString alloc] init]; 

    for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { 
     CFDictionaryRef dict = CFDictionaryGetValue(vals, keys[i]); 
     CFArrayRef values = CFDictionaryGetValue(dict, kSecPropertyKeyValue); 
     if (values == NULL) 
      continue; 
     [longDesc appendFormat:@"%s:%@\n\n", labels[i], [NSString stringFromDNwithSubjectName:values]]; 
    } 

    CFRelease(vals); 
    CFRelease(summaryRef); 
    CFRelease(commonNameRef); 

    return longDesc; 
} 

第二個功能是在上面嘗試提取任何你能得到你的手套上:

+(NSString *)stringFromDNwithSubjectName:(CFArrayRef)array { 
    NSMutableString * out = [[NSMutableString alloc] init]; 
    const void *keys[] = { kSecOIDCommonName, kSecOIDEmailAddress, kSecOIDOrganizationalUnitName, kSecOIDOrganizationName, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName }; 
    const void *labels[] = { "CN", "E", "OU", "O", "L", "S", "C", "E" }; 

    for(int i = 0; i < NVOID(keys); i++) { 
     for (CFIndex n = 0 ; n < CFArrayGetCount(array); n++) { 
      CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n); 
      if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) 
       continue; 
      CFTypeRef dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel); 
      if (!CFEqual(dictkey, keys[i])) 
       continue; 
      CFStringRef str = (CFStringRef) CFDictionaryGetValue(dict, kSecPropertyKeyValue); 
      [out appendFormat:@"%s=%@ ", labels[i], (__bridge NSString*)str]; 
     } 
    } 
    return [NSString stringWithString:out]; 
} 
+0

SecCertificateCopyValues在iOS上可用嗎? – Freedom 2013-03-11 11:03:29

+4

恐怕它只適用於OSX – anavarroma 2013-06-18 13:13:10

+3

這僅適用於OS X,因此它不回答問題。 – Sulthan 2014-07-07 13:53:53

0

僅供參考,假設你使用HTTPS,檢查CN自己是大多無用,因爲操作系統已經檢查確認名稱存在於證書中。你更可能想檢查公鑰(用於密鑰鎖定),你可以從信任對象獲取公鑰,而不需要直接觸摸證書。

如果公鑰與前一個關鍵字匹配,那麼該網站是合法的,或者有人徹底破壞了該網站。