IOS NSURLAuthenticationMethodClientCertificate not requested against ActiveSync server

I am trying to implement certificate authentication in the ActiveSync client that I am developing. The code used to use the auth certificate may work, but at the moment the server, or rather, the interpretation of the server’s responses to the iOS server, seems to me wrong. Here is my code:

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSURLProtectionSpace *protectionSpace = [challenge protectionSpace]; NSString *authenticationMethod = [protectionSpace authenticationMethod]; if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) { NSURLCredential* credential = [ self buildCredentialClientCert]; if ( credential == nil ) { [[challenge sender] cancelAuthenticationChallenge:challenge]; } else { [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } } else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { .... // do other stuff 

The problem is that I know that the server supports the auth client certificate, when I set a breakpoint, then authenticationMethod always set to NSURLAuthenticationMethodServerTrust .

The raw HTTPS server response contains the following:

Error Code: 403 Forbidden. The page requires a client certificate as part of the authentication process. If you are using a smart card, you will need to insert a smart card to select the appropriate certificate. Otherwise, contact the server administrator. (12213)

My question is what determines if the authentication request is NSURLAuthenticationMethodServerTrust compared to NSURLAuthenticationMethodClientCertificate ?

+6
source share
1 answer

Since no one answered this, and I finally came to a working solution, here it is.

Server trust is not a call from server to client, it allows the client to verify the trust offered by the server. With that in mind, the code below does not test trust, but it can.

Usually you get NSURLAuthenticationMethodServerTrust and then get NSURLAuthenticationMethodClientCertificate. This is not so - or. Here is the working code.

 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSURLProtectionSpace *protectionSpace = [challenge protectionSpace]; NSString *authenticationMethod = [protectionSpace authenticationMethod]; if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] && self.accountCertKeychainRef != nil) { SecIdentityRef identity = [KeychainUtilities retrieveIdentityWithPersistentRef:self.accountCertKeychainRef]; NSURLCredential* credential = [CertificateUtilities getCredentialFromCert:identity]; if ( credential == nil ) { [[challenge sender] cancelAuthenticationChallenge:challenge]; } else { [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } } else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; } else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM] || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) { self.lastProtSpace = [challenge protectionSpace]; if ([challenge previousFailureCount] > 2) { [[challenge sender] cancelAuthenticationChallenge:challenge]; } else { [[challenge sender] useCredential:[self buildCredential] forAuthenticationChallenge:challenge]; } } else { [[challenge sender] cancelAuthenticationChallenge:challenge]; } } 

For the question below, how can you get an identity:

 + (SecIdentityRef)copyIdentityAndTrustWithCertData:(CFDataRef)inPKCS12Data password:(CFStringRef)keyPassword { SecIdentityRef extractedIdentity = nil; OSStatus securityError = errSecSuccess; const void *keys[] = {kSecImportExportPassphrase}; const void *values[] = {keyPassword}; CFDictionaryRef optionsDictionary = NULL; optionsDictionary = CFDictionaryCreate(NULL, keys, values, (keyPassword ? 1 : 0), NULL, NULL); CFArrayRef items = NULL; securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); if (securityError == errSecSuccess) { CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0); // get identity from dictionary extractedIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity); CFRetain(extractedIdentity); } if (optionsDictionary) { CFRelease(optionsDictionary); } if (items) { CFRelease(items); } return extractedIdentity; } 

For those who are interested, here is getCredentialForCert:

 + (NSURLCredential *)getCredentialFromCert:(SecIdentityRef)identity { SecCertificateRef certificateRef = NULL; SecIdentityCopyCertificate(identity, &certificateRef); NSArray *certificateArray = [[NSArray alloc] initWithObjects:(__bridge_transfer id)(certificateRef), nil]; NSURLCredentialPersistence persistence = NSURLCredentialPersistenceForSession; NSURLCredential *credential = [[NSURLCredential alloc] initWithIdentity:identity certificates:certificateArray persistence:persistence]; return credential; } 
+13
source

All Articles