Client certificate authentication in UIWebView iOS

前端 未结 1 1917
生来不讨喜
生来不讨喜 2021-02-01 00:05

I\'m kind of new in objective c, but I\'m developing an app which has a UIWebView that loads some web content. All the web pages require client certificate for authentication an

相关标签:
1条回答
  • 2021-02-01 00:24

    To avoid any problem in the UIWebView, you have to make a reques to you website root, with the client certificate, before the request of the web view. You can use the UIWebViewDelegate method:

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    

    After this, the UIWebView will be able to load everything without any problem.

    If you are new to Objective-C, I guess you are also new to the Foundation framework so here's a bit of help.

    To solve this, I used ASIHTTPRequest as it was already embedded in our project. But you can use a NSURLConnection and do the logic in the NSURLConnectionDelegate method:

    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    

    So, here's my code to provide a client certificate to an ASIHTTPRequest prior to a UIWebView request:

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    {
    
      SecIdentityRef identity = NULL;
      SecTrustRef trust       = NULL;  
      NSData *PKCS12Data      = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test.cert" ofType:@"pfx"]];
    
      [self extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
    
      NSURL *serverUrl              = [NSURL URLWithString:URL_SECURE_SERVER];
      ASIHTTPRequest *firstRequest  = [ASIHTTPRequest requestWithURL:serverUrl];
    
      [firstRequest setValidatesSecureCertificate:NO];
      [firstRequest setClientCertificateIdentity:identity];
      [firstRequest startSynchronous];
    
      return YES;
    }
    

    I'm sending the request synchronously to ensure its completion before letting the UIWebView starts its loading.

    I use a method to retrieve the identity from the certificate, which is:

    - (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
    {
      OSStatus securityError          = errSecSuccess;
      NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"mobigate" forKey:(id)kSecImportExportPassphrase];
    
      CFArrayRef items  = CFArrayCreate(NULL, 0, 0, NULL);
      securityError     = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);
    
      if (securityError == 0) { 
        CFDictionaryRef myIdentityAndTrust  = CFArrayGetValueAtIndex (items, 0);
        const void *tempIdentity            = NULL;
    
        tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
    
        const void *tempTrust = NULL;
    
        tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
    
      } 
      else {
        NSLog(@"Failed with error code %d",(int)securityError);
        return NO;
      }
    
      return YES;
    }
    

    Here the same technique, but using the NSURLConnection instead of the ASIHTTPRequest

    • get your SecIdentityRef and your SecCertificateRef
    • create a NSURLCredential with those infos
    • send back this NSURLCredential to the [challenge sender] in the connection:didReceiveAuthenticationChallenge: method

    to use a certificate with a NSURLConnection, you have to implement the the NSURLConnectionDelegate method:

    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    

    In this method, the NSURLConnection is telling you that it received a challenge. You will have to create a NSURLCredential to send back to the [challenge sender]

    So you create your NSURLCredential:

    + (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence
    {
    
      NSString *certPath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];
      NSData *certData   = [[NSData alloc] initWithContentsOfFile:certPath];
    
      SecIdentityRef myIdentity;  // ???
    
      SecCertificateRef myCert = SecCertificateCreateWithData(NULL, (CFDataRef)certData);
      [certData release];
      SecCertificateRef certArray[1] = { myCert };
      CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
      CFRelease(myCert);
      NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity
                                  certificates:(NSArray *)myCerts
                                   persistence:NSURLCredentialPersistencePermanent];
      CFRelease(myCerts);
    }
    

    And finally use it with

    - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    

    on [challenge sender]

    You should have everything needed. Good luck.

    0 讨论(0)
提交回复
热议问题