Tags

, , , , , ,

Some time ago I blogged about client/server implementation for two way SSL ( both client and server are authenticated with certs ) on top of nodeJS, ( blog post ). Now, I’m trying to connect iOS client to nodeJS HTTPS server.

I will use NSURLConnection for requests. Also, client authentication is only possible with asynchronized requests, simplified sendSynchronousRequest doesn’t support delegates. I will load p12 generated certificate from App resources for authentication ( maybe better idea is to use Keychain for “production” applications but here I just want to test things )

Example Xcode project is available on HttpSSLClient – GitHub

So, when request is made to HTTPS server which requires authentication with certificate delegate’s connection:didReceiveAuthenticationChallenge is called. In this method we need to obtain certificate ( for code simplicity I will load cert each time from resource ) and present it to sender which will use that certificate against server.

Note: I’m playing here with self signed certificates and code didn’t work until I added this ( thanks to guy that found this ). Also, for simplicity I’m returning YES without any validation here.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return YES;
}

Back on track, to load certificate I’m using:

NSString *path = [[NSBundle mainBundle] pathForResource:@"userA" ofType:@"p12"];
NSData *p12data = [NSData dataWithContentsOfFile:path];
CFDataRef inP12data = (__bridge CFDataRef)p12data;
        
SecIdentityRef myIdentity;
SecTrustRef myTrust;
OSStatus status = extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
    
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = { myCertificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);

extractIdentityAndTrust function is copied from Apple Certificate, Key, and Trust Services Programming Guide with slightly modifications:

OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
    OSStatus securityError = errSecSuccess;
    
    CFStringRef password = CFSTR("userA");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import(inP12data, options, &items);
    
    if (securityError == 0) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        *identity = (SecIdentityRef)tempIdentity;
        const void *tempTrust = NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
        *trust = (SecTrustRef)tempTrust;
    }
    
    if (options) {
        CFRelease(options);
    }
    
    return securityError;
}

After certificate is loaded we need NSURLCredential that will be sent to certificate challenger

NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistencePermanent];
    
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];

For debug I’m using nodeJS server and certificate for userA is taken from that Git repo.

Links:

NSURLConnection class
Apple URL loading guide
Apple Certificate, Key, and Trust Services Programming Guide
NSURLConnection with Self-Signed Certificates
How to use Client Certificate Authentication in iOS App
How to connect with client certificate using a WebView in Cocoa?

About these ads