Client Certificate Authentication between an Android App and Tomcat7

NOTE

This post builds upon my previous post HTTPS Communication between an Android App and Tomcat7 using Self-Signed Certificates. Please see that post for the fundamentals of HTTPS communication and how Android Apps can authenticate the server.

Configuring Tomcat7 to do Client Certificate Authentication

Create the Client App’s Private Key in PEM format

$ openssl genrsa -des3 -out <client private key name>.pem 2048

Create the Client App’s Self-Signed Digital Certificate in PEM format

$ openssl req -new -x509 -key <client private key name>.pem -out <client ssl certificate name>.pem -days 7300

Create the Client’s KeyStore containing its Self-Signed Digital Certificate

  • Java’s keytool does not allow importing an existing private key into a keystore
  • Hence, we first use openssl to import the private key into a PKCS12 format keystore
$ openssl pkcs12 –export –inkey <client private key name>.pem –in <client ssl certificate name>.pem –out <client keystore name>.p12
  • We then convert the PKCS12 format keystore into JKS format so that it can also be used by Tomcat7
$ keytool –importkeystore –srckeystore <client keystore name>.p12 –srcstoretype pkcs12 –destkeystore <client keystore name>.jks –deststoretype jks
  • The client’s keystore will also be used as Tomcat7's truststore to verify clients’ authenticity

Configure Tomcat7 to use the Client KeyStore as its TrustStore

  • Copy the client’s keystore to your Tomcat7 server
  • Set the Connector to require Client Certificate Authentication
  • Update the Connector in the server.xml file in /etc/tomcat7/
  • Set clientAuth="true", truststoreFile="<Full Path to the TrustStore File>", trustStorePass="<TrustStore Password>"
<Connector port="443" protocol="HTTP/1.1" 
  SSLEnabled="true" maxThreads="150" scheme="https" 
  secure="true" clientAuth="true"   
  sslProtocol="TLS"
  keystoreFile="<Full Path to the Server's Keystore File>"
  keystorePass="<Keystore Password>"
  truststoreFile="<Full Path to the Client's Keystore File that you just copied to the Server>"
  truststorePass="<Client's Keystore Password>" />

Update the Android App to use the Client KeyStore

  • Copy the Client’s PKCS12 KeyStore to the Assets Folder, e.g., /assets/<client keystore name>.p12

Load the KeyStore

private KeyStore loadKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
  String KEY_STORE_FORMAT = "PKCS12";
  String KEY_STORE_FILENAME = "<client keystore name>.p12";
  String KEY_STORE_PASSWORD = "<Client's Keystore Password>";
  KeyStore keyStore = null;

  // Create a new keyStore object
  try {
    keyStore = KeyStore.getInstance(KEY_STORE_FORMAT);
  } catch (KeyStoreException e) {
    throw e;
  }

  // Open the keyStore file for reading  AssetManager
  assetManager = this.getContext().getAssets();
  InputStream inputStream = null;

  try {
    inputStream = assetManager.open(KEY_STORE_FILENAME, AssetManager.ACCESS_UNKNOWN);
  } catch (IOException e) {
    throw e;
  }

  // Load the keyStore
  try {
    keyStore.load(inputStream, KEY_STORE_PASSWORD.toCharArray());
  } catch (NoSuchAlgorithmException | CertificateException e) {
    throw e;
  }

  inputStream.close();

  return keyStore;
}

Create a KeyManagerFactory based on the KeyStore

private KeyManagerFactory initializeKeyManagerFactory(KeyStore keyStore, String keyStorePassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {

  KeyManagerFactory keyManagerFactory =   KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

  keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());

  return keyManagerFactory;

}

Update the creation of the SSLContext to include the KeyManagerFactory

private SSLContext initializeSSLContext(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustManagerFactory) throws NoSuchAlgorithmException, KeyManagementException {

  String CRYPTO_PROTOCOL = "TLS";

  SSLContext sslContext = SSLContext.getInstance(CRYPTO_PROTOCOL);

  sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

  return sslContext;

}

Thats it! Now use this SSLContext in your HttpsUrlConnection!

References