browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

Sending a client certificate during SSL handshake in Java/Android

Posted by on October 13, 2015

My current Android development assignment has me implementing an SSL connection to a server. There are lots of good blog posts and documentation out there about how to do this in Android, or Java in general. The scenario I’m working on is a bit less typical in that the client app has to send a certificate to authenticate itself to the server. It took me a fair bit of trial and error to figure out how to get it to work, so this is one of those posts that you write in case it helps somebody in a similar situation, possibly even your future self. 🙂

The Android docs give some guidance on this scenario, but unfortunately they don’t tell how to set up the KeyStore containing the client certificate:

KeyStore keyStore = ...;

I had trouble getting it set up. I tried creating an empty KeyStore, and generating a certificate to put in it from a raw resource PEM file:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
// use cert from our raw resource client.pem
InputStream certInput = mContext.getResources().openRawResource(R.raw.client);
Certificate clientCert;
try {
    clientCert = cf.generateCertificate(certInput);
    Log.d(TAG, "client cert=" + ((X509Certificate) ca).getSubjectDN());
} finally {
    certInput.close();
}

String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
// Put the cert into the new, empty keystore.
keyStore.load(null, null); // initialize
keyStore.setCertificateEntry("client", clientCert);

But I got parse errors on the cf.generateCertificate() call, such as “error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag”. Apparently the certificate’s PEM file wasn’t in the right format. What was wrong? Was it using DER instead of BER? How can one tell?

Skipping over tedious details, what eventually worked for me was this. My colleague, our server admin, had given me the client certificate in both .pem and .p12 formats. I read here that Java keytool “can treat a PKCS12 file as a keystore.” So instead of creating an empty keystore and reading in a certificate from a raw resource in PEM format, I tried loading the keystore directly from a raw resource in PKCS12 (.p12) format. It worked! Here’s the code:

KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
// We have deleted the raw resource client.pem and added client.p12 instead.
InputStream certInput = mContext.getResources().openRawResource(R.raw.client);
keyStore.load(certInput, "mypassword".toCharArray());

And now we can continue with the code provided in the Android documentation:

// Create a KeyManager that uses our client cert
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, null);

// Create an SSLContext that uses our TrustManager and our KeyManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

URL url = new URL("https://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());

As for the “BC” provider in KeyStore.getInstance("PKCS12", "BC"), I confess I don’t know much about it. I copied it from some examples I found. “BC” likely stands for BouncyCastle, but how do we know what providers are available, and what provider you need? No idea. But it works in this case.

3 Responses to Sending a client certificate during SSL handshake in Java/Android

  1. stonerain

    Thank you very much !
    Your article saves me.
    In my case “BC” is not needed.

  2. drinor

    I try this but I get “Trust anchor for certification path not found”.
    The code is here: http://stackoverflow.com/questions/39529040/android-http-request-with-client-certificate

    Do you have any idea?

Leave a Reply

Your email address will not be published. Required fields are marked *