Note: This was originally posted by me at the Intrepidus Group blog (web.archive.org) on October 6, 2011, and is a bit dated. Reproduced here for posterity.

This is a post on implementing mutual authentication while programming for Android and iOS. For the sake of brevity and focusing on the technical side of things, I’m assuming here that:

  1. You know what client authentication is
  2. You know how to issue certificates to your clients
  3. You know how to configure IIS/Apache/webserver_that_floats_your_boat for client authentication
  4. You know what I’m doing if I say “R.raw.resourceName” in Android, OR if I say I’m implementing a delegate in iOS
  5. You have a DER-encoded certificate for the root issuer of your SSL certificate (rootCert.cer), and a PKCS#12 object containing the client authentication certificate with the private key and chain of issuing certificates (clientCert.p12).

You’ll notice that the title says “Mutual Authentication”, not “Client Authentication” – our goal here is to implement both strong client authentication using certificates, and verify that the server certificate presented to us is issued by a CA we trust explicitly. For example, if my server certificate is obtained from Verisign, I would like to verify that the SSL certificate chains up to the Verisign root, instead of say the Comodo or DigiNotar roots, or any other root stored in the system certificate store.

Now that we have our logistics out of the way:

Android

For Android, we’ll be using the SSLSocketFactory class to implement client authentication. Place all your trusted root issuer certificates into a Bouncy Castle store (BKS). You can create a Bouncy Castle store by doing the following:

keytool -import -alias "rootCert" -file rootCert.cer -keystore myApp.truststore

For the application below, I’m assuming that the trust store and the client certificate are added as resources to the application. In an actual application, the client certificate is probably something that is enrolled for and obtained after the application has been distributed.

KeyStore serverRootCert = KeyStore.getInstance("BKS");
serverRootCert.load(getResources().openRawResource(R.raw.sslapptruststore), "password".toCharArray());

KeyStore clientCert = KeyStore.getInstance("pkcs12");
clientCert.load(getResources().openRawResource(R.raw.clientpass), "password".toCharArray());

This loads the truststore containing the trusted root certificates, and the client certificate we need to use for authentication.

HttpClient httpClient = null;
HttpParams httpParams = new BasicHttpParams();
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(clientCert, null, serverRootCert); 
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sslSocketFactory, 443));
httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams, registry), httpParams);

The code above creates a standard SSLSocketFactory object, setting our trust store as the certificate store we want to compare against, and adding the client certificate we want to use for authentication. The HttpClient object can then be used as you normally would in an application – i.e. set the URL we want to access, execute the request, as shown below:

HttpGet request = new HttpGet("https://example.com/clientAuthRequired.html");
HttpResponse response = httpClient.execute(request);

iOS

iOS follows a slightly different paradigm then Android. To implement client authentication, you need to override/implement specific NSURLConnection delegates.

There are a few basic steps to be kept in mind:

  • Define the URL resource you want to retrieve
  • Specify the authentication methods you want to handle
  • Create security objects for the authentication method
  • Handle the authentication challenge
NSURL *url = [NSURL URLWithString:@”https://example.com/clientAuthRequired.html”];
if(nil == url)
// handle error

That completes the complex process of defining a URL

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
    return YES;
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodClientCertificate])
    return YES;
  return NO;
}

This tells iOS that we would like to handle the Server Trust and Client Authentication events. If you want only client authentication, and want iOS to handle your trust, the server trust part can be skipped.

NSData *rootCertData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”rootCert ofType:@”cer]];
SecCertificateRef rootCertRef = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData);

NSData *p12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@“clientCert" ofType:@"p12"]];
NSArray *item = nil;
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@“password", kSecImportExportPassphrase, nil];
SecPKCS12Import((CFDataRef) p12Data , (CFDictionaryRef)dict, (CFArrayRef *)item);
SecIdentityRef identity = (SecIdentityRef)[[item objectAtIndex:0] objectForKey:(id)kSecImportItemIdentity];

Analogous to the Android code, we first load the resources we need to authenticate and verify identity.

-(void) connection:didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { // Verify method

    SecTrustRef trust = [[challenge protectionSpace] serverTrust];         // Create trust object
    NSArray *trustArray = [NSArray arrayWithObjects:rootCertRef, nil];   // Add as many certificates as needed
    SecTrustSetAnchorCertificates(trust, (CFArrayRef) trustArray );        // Set trust anchors

    SecTrustResultType trustResult;                                        // Store trust result in this
    SecTrustEvaluate(trust, trustResult);                                  // Evaluate server trust
    if(trust_result == kSecTrustResultUnspecified) {
      NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];
      [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
  } else {
    // handle error;
  }

  if([[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil   persistence:NSURLCredentialPersistenceNone];
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
  }
}

For limiting server trust, the list of root certificates we explicitly trust is set as the trust store to compare against. SecTrustSetAnchorCertificates overrides the system settings for making trust decisions, and replaces them with your own. For client authentication, the client certificate we loaded previously needs to be added as an NSURLCredential to the connection, and then let the networking do it’s thing.