Reputation: 51
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
Upvotes: 3
Views: 5220
Reputation: 7706
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).
Upvotes: 2