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 and I'm struggling with it for dew days. Does anyone know the flow how to implement it in UIWebView?
Thanks!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With