SSL Pinning on Android

richa123
3 min readJul 24, 2015

--

SSL Pinning is not a new concept, even then most mobile developers secure their apps by pinning their SSL certificates, only after they get a love letter from unknown hacker, looking for a fun project and some attention.

What is SSL Pinning?

Using SSL for network connections is the de facto method of ensuring secure data transmission in today’s mobile apps. Some apps that use SSL are not taking the extra step necessary to ensure eavesdropping cannot occur on the data connection. This “extra step” is known as SSL Pinning.

Source — https://possiblemobile.com/2013/03/ssl-pinning-for-increased-app-security

So what is that extra step?

It is to restrict your app to only talk to web services which exchange only one or a set of certificates that it can recognize, by storing it locally within the app.

But why this extra step, even for SSL connections?

It is possible, that a hacker can install a trusted certificate on their device, by intercepting it in one of the network exchanges between your app and your web service and use that to snoop on all future exchanges, to learn more about how your web service behaves. Also a hacker can install a fake certificate, that looks like it is signed by a trusted authority and send malware to your client. All of this can be stopped by pinning your server’s certificates in your app.

But this has one caveat, as a result of pinning, the certificates stored on the app need to be updated when the server certificates expire or change.

How to implement SSL Pinning on Android?

Implementing SSL Pinning is a lot simpler than one may think.

A brief overview of SSL and HTTPS on Android is covered here. To clarify the pinning part of it, this is what one needs to do.

  1. Load trusted CAs from resources
// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// Can be stored in res/raw
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}

2. Create a keystore containing trusted certificates

// Create a KeyStore containing trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

3. Create a custom TrustManager from the trusted CAs in the keystore

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

From this point on, how the trusted connection between your app and your web service will use this Trust Manager depends on the networking library or the http client your used in your app.

If using standard Android, create an SSLContext from the TrustManager and use that instead of the default SSLContext to create an HttpsUrlConnection

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

// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://mywebserice/getMyAssets");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

Chances are though that you may be using a networking library for a simple, easy and hassle free life. Chances are high that library may be OkHttp or Retrofit on Android. In this case, you can achieve the outcome from above in lot lesser steps

SSL Pinning with Retrofit

Follow Steps 1 above to load your web servers’ certificates.

Below is the 2nd step.


OkHttpClient client = new OkHttpClient();
String certPin = CertificatePinner.pin(ca);
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(MY_API_HOST, certPin)
.build();
client.setCertificatePinner(certificatePinner);

That’s it. Done.

Take this client and use it as your http client or add it to the RestAdapter in your Retrofit service configuration, and you are done!

--

--