2014-01-22 3 views
5

Я пытаюсь использовать аутентификацию сертификата клиента для доступа к защищенному веб-сайту. Код, который я использую, отлично работает в iOS 6.1, но не работает, когда сервер возвращает ошибку 403.7 при использовании iOS 7.Аутентификация сертификата клиента в iOS UIWebView Работает на iOS 6.1, но не iOS 7

Я использую соединение: willSendRequestForAuthenticationChallenge обработчик для проверки метода аутентификации и предоставления сертификата клиента.

Мой код:

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

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
    { 
     NSLog(@"Trust Challenge"); 
     SecTrustResultType trustResultType; 
     OSStatus err = SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustResultType); 

     NSLog(@"SecTrustResult %u %d",trustResultType, (int)err); 

     if (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultConfirm || trustResultType == kSecTrustResultUnspecified) { 
      [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; 
     } 
     else{ 
      [challenge.sender cancelAuthenticationChallenge:challenge]; 
     } 

    } else { 
     NSString *path = [[NSBundle mainBundle]pathForResource:@"mycert" ofType:@"pfx"]; 
     NSData *p12data = [NSData dataWithContentsOfFile:path]; 

     CFDataRef inP12data = (__bridge CFDataRef)p12data; 

     SecIdentityRef myIdentity; 
     SecTrustRef myTrust; 
     extractIdentityAndTrust(inP12data, &myIdentity, &myTrust); 
     assert(myIdentity != nil); 
     assert(myTrust != nil); 

     long count = SecTrustGetCertificateCount(myTrust); 
     NSMutableArray* myCertificates = nil; 
     if(count > 1) { 
      myCertificates = [NSMutableArray arrayWithCapacity:count]; 
      for(int i = 1; i < count; ++i) { 
       [myCertificates addObject:(__bridge id)SecTrustGetCertificateAtIndex(myTrust, i)]; 
      } 
     } 

     NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:myCertificates persistence:NSURLCredentialPersistenceNone]; 
     assert(credential != nil); 

     NSLog(@"User: %@, certificates %@ identity:%@", [credential user], [credential certificates], [credential identity]); 
     [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; 
    } 
} 

Я использую эту функцию, чтобы извлечь содержимое сертификата:

OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust) 
{ 
    OSStatus securityError = errSecSuccess; 

    CFStringRef password = CFSTR("password"); 
    const void *keys[] = { kSecImportExportPassphrase }; 
    const void *values[] = { password }; 

    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); 

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); 
    securityError = SecPKCS12Import(inP12data, options, &items); 

    if (securityError == 0) { 
     CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0); 
     const void *tempIdentity = NULL; 
     tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity); 
     *identity = (SecIdentityRef)tempIdentity; 
     const void *tempTrust = NULL; 
     tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust); 
     *trust = (SecTrustRef)tempTrust; 

     CFIndex count = CFArrayGetCount(items); 
     NSLog(@"Certificates found: %ld",count); 
    } 

    if (options) { 
     CFRelease(options); 
    } 

    return securityError; 
} 

Файл mycert.pfx содержит промежуточный сертификат вместе с клиентом серт.

Соединение: функция didFailWithError никогда не вызывается.

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
     NSLog(@"Error: %@", [error userInfo]); 
    } 

Таким образом, похоже, что согласование сертификата успешно выполняется на некотором уровне.

Моя проблема аналогична SSL - behaves differently in iOS7?, но я использую Windows Server 2008 R2 с IIS 7.5. На сервере были включены протоколы TLS 1.1 и TLS 1.2.

Трассировка WireShark показывает, что рамка сертификата во время установления связи TLS пуста при использовании iOS 7. Сертификат отправляется и проверяется при использовании iOS 6.1.

Я могу получить доступ к сайту в iOS 7 с помощью Safari.

Любая помощь очень ценится.

ответ

3

Мне удалось найти решение с помощью поддержки Apple Developer. Решение включает создание пользовательского NSURLProtocol. Я использовал образец кода Apple по адресу https://developer.apple.com/library/ios/#samplecode/CustomHTTPProtocol/. Пример кода показывает, как переопределить оценку доверия HTTPS-сервера, поэтому его необходимо изменить для работы с аутентификацией сертификата клиента.

Я модифицировал функцию AppDelegate didRecieveAuthenticationChallenge.

- (void)customHTTPProtocol:(CustomHTTPProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
// A CustomHTTPProtocol delegate callback, called when the protocol has an authenticate 
// challenge that the delegate accepts via - customHTTPProtocol:canAuthenticateAgainstProtectionSpace:. 
// In this specific case it's only called to handle server trust authentication challenges. 
// It evaluates the trust based on both the global set of trusted anchors and the list of trusted 
// anchors returned by the CredentialsManager. 
{ 
    OSStatus   err; 
    NSURLCredential * credential; 

    assert(protocol != nil); 
    assert(challenge != nil); 

    credential = nil; 

    // Handle ServerTrust and Client Certificate challenges 

    NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod]; 
    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 
     NSLog(@"Trust Challange"); 
     SecTrustResultType trustResultType; 
     err = SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustResultType); 

     NSLog(@"SecTrustResult %u %d",trustResultType, (int)err); 

     if (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultUnspecified) { 
      credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; 
      assert(credential != nil); 
     } 
    } else { 
     NSString *path = [[NSBundle mainBundle]pathForResource:@"mycert" ofType:@"pfx"]; 
     NSData *p12data = [NSData dataWithContentsOfFile:path]; 

     SecIdentityRef identity = NULL; 
     SecCertificateRef certificate = NULL; 

     [Util identity:&identity andCertificate:&certificate fromPKCS12Data:p12data withPassphrase:@"asia1215"]; 

     assert(identity != NULL); 

     NSArray *certArray = [NSArray arrayWithObject:(__bridge id)certificate]; 
     credential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent]; 
    } 

    [protocol resolveAuthenticationChallenge:challenge withCredential:credential]; 
} 
+0

Это также работает с запросами Ajax? – ridan

+1

Да, он работает с запросами Ajax. –

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