2017-04-25 9 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, 귀하의 프로젝트에 대한 답변과 링크에 대해 매우 감사드립니다. 나는 그것을 보았다. 그리고 그것은 나의 프로젝트를 위해 많이 진화하기 위해 약간있다. 가장 간단한 방법은 내 cer 인증서를 der로 변환하고 서버와 로컬 바이트를 비교하여 연결을 신뢰하는 것입니다. –