I am trying to implement https client certificate authentication in iOS with NSURLSession
. Here is what I am doing:
-(void) httpPostWithCustomDelegate :(NSDictionary *) params { NSString *ppyRequestURL = [NSString stringWithFormat:@"%@/fetchcountryCities", PPBaseURL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ppyRequestURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request setHTTPMethod:@"POST"]; [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { Log(@"ASDAD"); }]; [postDataTask resume]; }
I am providing the client certificate in challenge handler like this:
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler { if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) { NSURLCredential *credential = [self provideClientCertificate]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } }
Here is how I load my client certificate,
- (NSURLCredential *)provideClientCertificate { SecIdentityRef identity = [self findClientCertificate]; if (!identity) { return nil; } SecCertificateRef certificate = NULL; SecIdentityCopyCertificate (identity, &certificate); const void *certs[] = {certificate}; CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray *)certArray persistence:NSURLCredentialPersistencePermanent]; CFRelease(certArray); return credential; }
- (SecIdentityRef)findClientCertificate { SecIdentityRef clientCertificate = NULL; if (clientCertificate) { CFRelease(clientCertificate); clientCertificate = NULL; } NSString *pkcs12Path = [[NSBundle mainBundle] pathForResource:@"johndoe" ofType:@"p12"]; NSData *pkcs12Data = [[NSData alloc] initWithContentsOfFile:pkcs12Path]; CFDataRef inPKCS12Data = (__bridge CFDataRef)pkcs12Data; CFStringRef password = CFSTR("password"); const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = NULL; OSStatus err = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); CFRelease(optionsDictionary); CFRelease(password); if (err == errSecSuccess && CFArrayGetCount(items) > 0) { CFDictionaryRef pkcsDict = CFArrayGetValueAtIndex(items, 0); SecTrustRef trust = (SecTrustRef)CFDictionaryGetValue(pkcsDict, kSecImportItemTrust); if (trust != NULL) { clientCertificate = (SecIdentityRef)CFDictionaryGetValue(pkcsDict, kSecImportItemIdentity); CFRetain(clientCertificate); } } if (items) { CFRelease(items); } return clientCertificate; }
Now when the API is called I am getting back this error:
Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSUnderlyingError=0x7f8428df4d40 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}
Whats going wrong here?