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