2017-04-25 156 views
0

我不是iOS和SSL釘扎專家。嘗試將本地證書添加到錨點以信任它們。Xcode的SSL寄託信任錨證書

嘗試了幾個代碼,總是得到一個kSecTrustResultRecoverableTrustFailure。

這段代碼有什麼問題? 我應該將cer轉換爲der嗎? 我應該刪除服務器證書並僅使用本地可信證書嗎?

任何想法,輸入?

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 

NSString * derCaPath; 
NSMutableArray * chain = [NSMutableArray array]; 

for(int i=0; i<= 3; i++) 
{ 
    if (i==0) 
     derCaPath = [[NSBundle mainBundle] pathForResource:@"dstrootcax3" ofType:@"cer"]; 
    if (i==1) 
     derCaPath = [[NSBundle mainBundle] pathForResource:@"comodorsacertificationauthority" ofType:@"cer"]; 
    if (i==2) 
     derCaPath = [[NSBundle mainBundle] pathForResource:@"geotrustglobalca" ofType:@"cer"]; 
    else 
     derCaPath = [[NSBundle mainBundle] pathForResource:@"thawteprimaryrootca" ofType:@"cer"]; 

    NSData *derCA = [NSData dataWithContentsOfFile:derCaPath]; 

    SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)derCA); 

    //NSArray * chain = [NSArray arrayWithObject:(__bridge id)(caRef)]; 

    [chain addObject:(__bridge id)(caRef)]; 
} 


caChainArrayRef = CFBridgingRetain(chain); 

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
{ 
    SecTrustRef trust = nil; 
    SecTrustResultType result; 
    OSStatus err = errSecSuccess; 

#if DEBUG 
    { 
     NSLog(@"Chain received from the server (working 'up'):"); 
     CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust); 
     for(int i = 0; i < certificateCount; i++) { 
      SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i); 
      //CFStringRef str = SecCertificateCopyLongDescription(kCFAllocatorDefault, certRef, nil); 
      //NSLog(@" %02i: %@", 1+i, str); 
      //CFRelease(str); 
     } 

     NSLog(@"Local Roots we trust:"); 
     for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) { 
      SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i); 
      //CFStringRef str = SecCertificateCopyLongDescription(kCFAllocatorDefault, certRef, nil); 
      //NSLog(@" %02i: %@", 1+i, str); 
      //CFRelease(str); 
     } 
    } 
#endif 

    if (checkHostname) { 
     // We use the standard Policy of SSL - which also checks hostnames. 
     // -- see SecPolicyCreateSSL() for details. 
     // 
     trust = challenge.protectionSpace.serverTrust; 
     // 
#if DEBUG 
     NSLog(@"The certificate is expected to match '%@' as the hostname", 
       challenge.protectionSpace.host); 
#endif 
    } else { 
     // Create a new Policy - which goes easy on the hostname. 
     // 

     // Extract the chain of certificates provided by the server. 
     // 
     CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust); 
     NSMutableArray * chain = [NSMutableArray array]; 

     for(int i = 0; i < certificateCount; i++) { 
      SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i); 
      [chain addObject:(__bridge id)(certRef)]; 
     } 

     for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) { 
      SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i); 
      [chain addObject:(__bridge id)(certRef)]; 
     } 


     // And create a bland policy which only checks signature paths. 
     // 
     if (err == errSecSuccess) 
      err = SecTrustCreateWithCertificates((__bridge CFArrayRef)(chain), 
               SecPolicyCreateBasicX509(), &trust); 
#if DEBUG 
     NSLog(@"The certificate is NOT expected to match the hostname '%@' ", 
       challenge.protectionSpace.host); 
#endif 
    }; 

    // Explicity specify the list of certificates we actually trust (i.e. those I have hardcoded 
    // in the app - rather than those provided by some randon server on the internet). 
    // 
    if (err == errSecSuccess) 
     err = SecTrustSetAnchorCertificates(trust,caChainArrayRef); 

    // And only use above - i.e. do not check the system its global keychain or something 
    // else the user may have fiddled with. 
    // 
    if (err == errSecSuccess) 
     err = SecTrustSetAnchorCertificatesOnly(trust, YES); 

    if (err == errSecSuccess) 
     err = SecTrustEvaluate(trust, &result); 


    if (err == errSecSuccess) { 
     switch (result) { 
      case kSecTrustResultProceed: 
       // User gave explicit permission to trust this specific 
       // root at some point (in the past). 
       // 
       NSLog(@"GOOD. kSecTrustResultProceed - the user explicitly trusts this CA"); 
       [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust] 
        forAuthenticationChallenge:challenge]; 
       goto done; 
       break; 
      case kSecTrustResultUnspecified: 
       // The chain is technically valid and matches up to the root 
       // we provided. The user has not had any say in this though, 
       // hence it is not a kSecTrustResultProceed. 
       // 
       NSLog(@"GOOD. kSecTrustResultUnspecified - So things are technically trusted. But the user was not involved."); 
       [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust] 
        forAuthenticationChallenge:challenge]; 
       goto done; 
       break; 
      case kSecTrustResultInvalid: 
       NSLog(@"FAIL. kSecTrustResultInvalid"); 
       break; 
      case kSecTrustResultDeny: 
       NSLog(@"FAIL. kSecTrustResultDeny (i.e. user said no explicitly)"); 
       break; 
      case kSecTrustResultFatalTrustFailure: 
       NSLog(@"FAIL. kSecTrustResultFatalTrustFailure"); 
       break; 
      case kSecTrustResultOtherError: 
       NSLog(@"FAIL. kSecTrustResultOtherError"); 
       break; 
      case kSecTrustResultRecoverableTrustFailure: 
       NSLog(@"FAIL. kSecTrustResultRecoverableTrustFailure (i.e. user could say OK, but has not been asked this)"); 
       // DM 25.04.2017 we allow connection for the moment 
       [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust] 
        forAuthenticationChallenge:challenge]; 
       goto done; 
       break; 
      default: 
       NSAssert(NO,@"Unexpected result: %d", result); 
       break; 
     } 
     // Reject. 
     [challenge.sender cancelAuthenticationChallenge:challenge]; 
     goto done; 
    }; 
    //CFStringRef str =SecCopyErrorMessageString(err,NULL); 
    //NSLog(@"Internal failure to validate: result %@", str); 
    //CFRelease(str); 

    [[challenge sender] cancelAuthenticationChallenge:challenge]; 

done: 
    if (!checkHostname) 
     CFRelease(trust); 
    return; 
} 
// In this example we can cancel at this point - as we only do 
// canAuthenticateAgainstProtectionSpace against ServerTrust. 
// 
// But in other situations a more gentle continue may be appropriate. 
// 
// [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 

NSLog(@"Not something we can handle - so we're canceling it."); 
[challenge.sender cancelAuthenticationChallenge:challenge]; 
} 

回答

0

你不應該這樣做你自己:這些API(因爲你可以看到)是非常棘手的,這是幾乎不可能得到驗證正確的,除非你既是SSL和iOS專家。

例如,kSecTrustResultRecoverableTrustFailure可能意味着很多事情,例如過期的證書;你不希望你的應用程序允許這個。與主機名驗證同樣的東西:它永遠不應該被禁用(即使對於調試:使用正確的主機名生成測試證書也很容易)。

我曾在一個SSL固定庫上工作,負責所有這些事情: https://github.com/datatheorem/TrustKit。您應該嘗試一下,因爲它是專門爲您的用例設計的。

+0

Nabla,非常感謝您的回答並鏈接到您的項目。我看了一下,這對我的項目有很大的發展。我認爲最簡單的方法是將我的證書轉換爲der,然後比較服務器和本地字節來信任連接。 –