Client certificate authentication in UIWebView iOS

不想你离开。 提交于 2019-12-02 17:48:02

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!