I am developing iOS Application. We have custom certificate authority with self-signed ca-cert. Certification authority issues certificates both for users and for https server too. I would like to create iOS application which can authenticate https server using ca certificate and also can communicate to https server using client certificate. I already have the code for communicating with https server using client certificate, but I need to have ca-certificate imported to system keyring. I would like to have ca certificate hard-coded into application. My code looks like this:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
bool result=NO;
if ([protectionSpace authenticationMethod] == NSURLAuthenticationMethodServerTrust) {
result= YES;
} else if([protectionSpace authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
result = YES;
}
return result;
}
- (BOOL)shouldTrustProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
CFDataRef certDataRef = (__bridge_retained CFDataRef)self.rootCertData;
SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef);
SecTrustRef serverTrust = protectionSpace.serverTrust;
CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL);
SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
SecTrustResultType trustResult;
SecTrustEvaluate(serverTrust, &trustResult);
return trustResult == kSecTrustResultUnspecified;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"Did receive auth challange %@",[challenge debugDescription]);
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSString *authMethod = [protectionSpace authenticationMethod];
if(authMethod == NSURLAuthenticationMethodServerTrust ) {
NSLog(@"Verifying The Trust");
NSURLCredential* cred=[NSURLCredential credentialForTrust:[protectionSpace serverTrust]];
if ([self shouldTrustProtectionSpace:challenge.protectionSpace]) {
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
NSLog(@"OK");
} else {
NSLog(@"FAILED");
[challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
}
}
if(authMethod == NSURLAuthenticationMethodClientCertificate ) {
NSLog(@"Trying Certificate");
.....
Everything works like a charm, until server does not require client certificate. At this moment I will receive error The certificate for this server is invalid and execution will never reach point
NSLog(@"Trying Certificate");
When I have .der ca-cert loaded into system keyring, everything works, even client certificate is sent to server, and server can recognize user. I thing that
SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
affects somehow trust, cause when I skip this call, I can simply do:
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
without any error, but I'm not able to verify certificate in this case.
What am I doing wrong?
Big thanks, Adam
Have a look at this code
https://github.com/dirkx/Security-Pinning-by-CA
which does both by fairly carefully keeping the two trust blocks separate (which you seem to be mixing).
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