2014-01-22 17 views
5

Ich versuche, die Clientzertifikatauthentifizierung für den Zugriff auf eine sichere Website zu verwenden. Der Code, den ich benutze funktioniert gut in iOS 6.1 aber schlägt mit dem Server einen 403.7 Fehler bei der Verwendung von iOS 7.Clientzertifikatsauthentifizierung auf iOS UIWebView Funktioniert auf iOS 6.1, aber nicht auf iOS 7

Ich benutze die Verbindung: WillSendRequestForAuthenticationChallenge Handler, um die Authentifizierungsmethode zu überprüfen und das Client-Zertifikat bereitzustellen.

Mein Code ist:

- (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]; 
    } 
} 

Ich benutze diese Funktion den Inhalt des Zertifikats zu extrahieren:

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; 
} 

Die mycert.pfx Datei enthält das Zwischenzertifikat zusammen mit dem Client-Zertifikat.

Die Verbindung: didFailWithError Funktion wird nie aufgerufen.

So scheint es, dass die Zertifikatsverhandlung auf einem gewissen Niveau erfolgreich ist.

Mein Problem ist ähnlich wie SSL - behaves differently in iOS7?, aber ich verwende Windows Server 2008 R2 mit IIS 7.5. Die Protokolle TLS 1.1 und TLS 1.2 wurden auf dem Server aktiviert.

Ein WireShark-Trace zeigt, dass der Zertifikatsrahmen während des TLS-Handshakes bei Verwendung von iOS 7 leer ist. Das Zertifikat wird gesendet und überprüft, wenn iOS 6.1 verwendet wird.

Ich kann die Website in iOS 7 mit Safari zugreifen.

Jede Hilfe wird sehr geschätzt.

Antwort

3

Ich konnte eine Lösung mit Hilfe von Apple Developer Support finden. Die Lösung umfasst das Erstellen eines benutzerdefinierten NSURLProtocols. Ich habe den Apple-Beispielcode unter https://developer.apple.com/library/ios/#samplecode/CustomHTTPProtocol/ verwendet. Der Beispielcode zeigt, wie die HTTPS-Server-Vertrauensbewertung überschrieben wird. Daher muss sie geändert werden, damit sie mit der Clientzertifikatauthentifizierung verwendet werden kann.

Ich modifizierte die AppDelegate didRecieveAuthenticationChallenge-Funktion.

- (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

Funktioniert das auch mit Ajax-Anfragen? – ridan

+1

Ja, es funktioniert mit Ajax-Anfragen. –

Verwandte Themen