0

我想實現HTTPS客戶端證書身份驗證的iOS與NSURLSession。下面是我在做什麼:的iOS NSURLErrorDomain代碼= -1005與ClientCertificate驗證挑戰

-(void) httpPostWithCustomDelegate :(NSDictionary *) params 
{ 
    NSString *ppyRequestURL = [NSString stringWithFormat:@"%@/fetchcountryCities", PPBaseURL]; 

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ppyRequestURL] 
                  cachePolicy:NSURLRequestUseProtocolCachePolicy 
                 timeoutInterval:60.0]; 

    [request setHTTPMethod:@"POST"]; 
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; 
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; 
    NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
     Log(@"ASDAD"); 
    }]; 
    [postDataTask resume]; 
} 

我提供這樣的質詢處理程序的客戶端證書:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{ 

    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){ 
     NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; 
     completionHandler(NSURLSessionAuthChallengeUseCredential,credential); 
    } 

    else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) { 
       NSURLCredential *credential = [self provideClientCertificate]; 
       completionHandler(NSURLSessionAuthChallengeUseCredential, credential); 
      } 
} 

這是我如何加載我的客戶端證書,

- (SecIdentityRef)findClientCertificate { 
    SecIdentityRef clientCertificate = NULL; 

    if (clientCertificate) { 
     CFRelease(clientCertificate); 
     clientCertificate = NULL; 
    } 

    NSString *pkcs12Path = [[NSBundle mainBundle] pathForResource:@"johndoe" ofType:@"p12"]; 
    NSData *pkcs12Data = [[NSData alloc] initWithContentsOfFile:pkcs12Path]; 

    CFDataRef inPKCS12Data = (__bridge CFDataRef)pkcs12Data; 
    CFStringRef password = CFSTR("password"); 
    const void *keys[] = { kSecImportExportPassphrase }; 
    const void *values[] = { password }; 
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); 
    CFArrayRef items = NULL; 

    OSStatus err = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); 

    CFRelease(optionsDictionary); 
    CFRelease(password); 

    if (err == errSecSuccess && CFArrayGetCount(items) > 0) { 
     CFDictionaryRef pkcsDict = CFArrayGetValueAtIndex(items, 0); 

     SecTrustRef trust = (SecTrustRef)CFDictionaryGetValue(pkcsDict, kSecImportItemTrust); 

     if (trust != NULL) { 
      clientCertificate = (SecIdentityRef)CFDictionaryGetValue(pkcsDict, kSecImportItemIdentity); 
      CFRetain(clientCertificate); 
     } 
    } 

    if (items) { 
     CFRelease(items); 
    } 

    return clientCertificate; 
} 

- (NSURLCredential *)provideClientCertificate { 
    SecIdentityRef identity = [self findClientCertificate]; 

    if (!identity) { 
     return nil; 
    } 

    SecCertificateRef certificate = NULL; 
    SecIdentityCopyCertificate (identity, &certificate); 
    const void *certs[] = {certificate}; 
    CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); 
    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray *)certArray persistence:NSURLCredentialPersistencePermanent]; 
    CFRelease(certArray); 

    return credential; 
} 

現在當API調用我取回此錯誤:

Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSUnderlyingError=0x7f8428df4d40 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}

我在模擬器和設備上收到相同的錯誤。我完全卡住了。不知道這裏發生了什麼問題。

*****更新***** 我就與查爾斯代理查詢,瞭解更多詳情。令我驚訝的是,當我將客戶端證書添加到charles proxy時,我收到服務器的響應,所以我錯過了plist中的一些設置或加載p12的問題?

從plist中的設置,

<key>NSAppTransportSecurity</key> 
    <dict> 
     <key>NSExceptionDomains</key> 
     <dict> 
      <key>test.mydomain.com</key> 
      <dict> 
       <key>NSExceptionAllowsInsecureHTTPLoads</key> 
       <true/> 
       <key>NSExceptionMinimumTLSVersion</key> 
       <string>TLSv1.2</string> 
       <key>NSExceptionRequiresForwardSecrecy</key> 
       <true/> 
       <key>NSIncludesSubdomains</key> 
       <true/> 
       <key>NSRequiresCertificateTransparency</key> 
       <false/> 
       <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key> 
       <false/> 
       <key>NSThirdPartyExceptionMinimumTLSVersion</key> 
       <string>TLSv1.2</string> 
       <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> 
       <true/> 
      </dict> 
     </dict> 
    </dict> 

回答

0

一目瞭然,我看到三個問題:

  • 你正在做一個POST請求不提供請求主體。這可能會導致請求失敗,甚至無法訪問服務器。

  • 你的服務器信任的處理,如寫的,能有效去除否則你會從TLS告訴操作系統盲目地信任它(我認爲)得到任何保護。

    您應該A.告訴NSURLSession在服務器信任案例中執行默認處理或B.自己檢查證書,然後然後告訴它使用證書。

  • 您的身份僅包括客戶端證書,而不是可能需要爲信任它的服務器的任何中間證書。

    您應該使用您找到的第一個身份,但將您在身份文件中找到的每個證書都加入證書數組(可能還有客戶證書本身,但我隱約地回想一下,你不應該在那裏添加它;嘗試兩種方式,看看哪一個失敗)。

請注意,如果您知道任何用戶的身份都沒有證書鏈,那麼第三個用戶的身份證可能無關緊要。

+0

感謝您的答覆。我嘗試了所有建議,但仍然收到同樣的錯誤。 – Zach

+0

我確實與charles proxy進行了檢查以瞭解更多詳細信息。令我驚訝的是,當我將客戶端證書添加到charles proxy時,我收到服務器的響應,所以我錯過了plist中的一些設置或加載p12的問題? – Zach

+0

等待......如果您正在瀏覽代理服務器,它會正常工作,但否則會失敗? – dgatwood