To avoid any problems in UIWebView, before requesting a web view, you need to make a request to your website root with a client certificate. You can use the UIWebViewDelegate method:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
After that, UIWebView will be able to load everything without problems.
If you are new to Objective-C, I think you are also new to the Foundation Framework, so help a little.
To solve this problem, I used ASIHTTPRequest since it was already built into our project. But you can use NSURLConnection and execute the logic in the NSURLConnectionDelegate method:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
So here is my code to provide the ASIHTTPRequest client certificate before requesting a UIWebView:
-(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 send the request synchronously to ensure that it completes before the UIWebView starts loading.
I use a method to obtain an identity from a certificate, which:
- (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 is the same method, but using NSURLConnection instead of ASIHTTPRequest
- get your SecIdentityRef and your SecCertificateRef
- create NSURLCredential with this information
- send this NSURLCredential to [request sender] in the connection: didReceiveAuthenticationChallenge: method
To use the certificate with NSURLConnection, you need to implement the NSURLConnectionDelegate method:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
In this method, NSURLConnection tells you that it has received a call. You will need to create an NSURLCredential to send back to the [sender of the request]
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;
And finally use it with
- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
to [call sender]
You must have everything you need. Good luck.