0

Я пытаюсь реализовать аутентификацию сертификата клиента https в iOS с NSURLSession. Вот что я делаю:iOS NSURLErrorDomain Code = -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, чтобы узнать более подробную информацию. К моему удивлению, когда я добавил сертификат клиента на 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, сообщая ОС, чтобы слепо доверять ей (я думаю).

    Вы должны либо указать, что NSURLSession выполнит обработку по умолчанию в деле доверия сервера, либо B. проверьте сертификат самостоятельно и , затем сообщите ему, чтобы использовать сертификат.

  • Ваша личность включает только клиентский сертификат, а не промежуточные сертификаты, которые могут потребоваться для сервера, чтобы доверять ему.

    Возможно, вы должны объединить эти два метода, используя первый идентификатор, который вы найдете, но взяв каждый сертификат, который вы найдете в файле идентификации, и добавив все их в массив сертификатов (возможно, вместе с самим сертификатом клиента, но я смутно напомните, что вы не должны добавлять его туда, попробуйте в обоих направлениях и посмотрите, какой из них не удается).

Помните, что если вы знаете, что ни одна из ваших идентификаторов ваших пользователей никогда не будет иметь цепочку сертификатов, то третий, вероятно, не имеет значения.

+0

Спасибо за ваш ответ. Я пробовал все ваши предложения, но все равно возвращал ту же ошибку. – Zach

+0

Я проверил с помощью прокси-сервера charles, чтобы узнать больше. К моему удивлению, когда я добавил сертификат клиента на charles proxy, я возвращаю ответ с сервера, так что я пропускаю некоторые настройки в plist или проблемы с загрузкой p12? – Zach

+0

Подождите ... это работает, если вы проходите через прокси-сервер, но в противном случае он терпит неудачу? – dgatwood

Смежные вопросы