Certificate Pinning for Mobile Android Apps [Java & React Native]

Florian Bergmann
applike
Published in
5 min readFeb 7, 2019

We at AppLike are always looking at solutions on how we can make our product more secure for our users, as well as, protecting our intellectual property and ensuring that our systems are not abused. We aim to stay up-to-date with the standards of the industry and look for methods to improve the quality of our products. This article will focus on one of these methods that ensures a higher safety standard of our mobile apps that are used by millions of users worldwide.

What is certificate pinning and when is it needed?

When using certificate (or public key) pinning, a mobile client only allows connections to a server that uses a trusted certificate. The pinned certificate can be any that is part of the certificate chain and it is possible to pin multiple certificates. Usually pinning the certificate that is at the beginning of a chain results in a higher security but needs to be changed out more often. Whereas pinning an intermediate certificate or even the root certificate will result in a higher amount of possible trusted certificates (meaning that the client will trust every certificate that is signed by the certificate authority (CA) of the pinned certificate).

There are two big reasons for using pinning for a mobile app. The first one is to ensure that your users data is better protected from a man-in-the-middle attack. Enabling certificate pinning prevents the usage of e.g. a proxy to read the requests data. The client’s connection will fail in this case and you will be able to notify your users that there might be a security issue.

Secondly, using certificate pinning also protects your app from someone that tries to tamper with it. Since our product is very unique in the adtech market and users are being awarded with real currency we are an appealing target for fraudulent behavior. Certificate pinning helps us to fight some of this fraudulent behavior by preventing a user from viewing and manipulating the data that is send to our servers.

Implementing Certificate Pinning for Android apps

Since we offer apps for Android that are written in Java, as well as, React-Native this part will show the implementation for both.

Java

There are several ways to implement pinning depending on what network solution is used. Most common network libraries already support certificate pinning. In our case we have our own network solution that uses HttpsURLConnection. This tutorial will explain how to add certificate pinning when using HttpsURLConnection.

First we need a list of certificates that should be pinned. There are several ways to do it and we choose to load them from the assets folder. This can be easily achieved in a few lines of Code:

https://github.com/applike/blog-references/blob/master/certificate-pinning-android/assets.java

In order to add certificate pinning we created a keystore that holds the pinned certificates:

https://raw.githubusercontent.com/applike/blog-references/master/certificate-pinning-android/keystore.java

Next a Trustmanager has to be created that uses our keystore. The TrustManager will then be added to our connection via an SSLContext:

https://raw.githubusercontent.com/applike/blog-references/master/certificate-pinning-android/trustmanager.java

Finished! Now your connections will be pinned.

ReactNative

For React Native the amount of available solutions is more limited. One library that we found which supports certificate pinning is pinch (https://github.com/localz/react-native-pinch). It was easy to implement and worked well. However with React Native 0.54+ a new feature was added that allows to create a custom OkHttp client and hand it to the OkHttpClientProvider that Fetch is using under Android. This allowed us to be independent of a third-party library and continue using Fetch but with added certificate pinning.

First we needed to create our own custom OkHttpClientFactory Java class that uses a CertificatePinner:

https://raw.githubusercontent.com/applike/blog-references/master/certificate-pinning-android/certificatepinner.java

The public key of the certificate can be easily found with the following shell command:

$ openssl s_client -connect your-domain:443 | openssl x509 -pubkey -noout

Next it has to be set in the onCreate of your Application class:

OkHttpClientProvider.setOkHttpClientFactory(new CertificatePinningClientFactory());

Thats it! Now all fetch requests should be using certificate pinning.

Best practices and how to prevent any issues.

Over time there are a few things that we learned while using certificate pinning that would be great to have known beforehand. Here are some tips and steps that you should be aware of when using it.

Implement it as soon as possible

Not every user updates regularly and once an old version without certificate pinning was available, it can be used to bypass the security gain that you have from it.

Always know when a pinned certificate expires

Depending on which certificates in the chain you are pinning the expiration date can vary. Especially the server certificate usually is only valid for a few months. Therefore it is import to be aware when the pinned certificate expires to allow enough time for a transition period before it changes.

Ensure a sufficient enough transitioning period when changing a server certificate

Before changing the pinned certificate you should introduce a transitioning period where your clients are pinning the current, as well as, the future certificate. This ensures that by the time you change the certificate on the server that most users have already updated to a version that supports the new certificate.

Notify a user when they are using an app version that is pinning an outdated version

When a user is still using an older version of your app that pins a certificated that is expired or will expire soon you should let them know that they need to update the app in order for it to continue working.

Have a backup plan when all pinned certificates are invalid

The worst case scenario is when the server certificate is different from the pinned certificates. In this case it would not be possible for your apps to make requests to your server anymore. Let’s say you forgot about the expiration of your certificate and therefore you did not plan for a transitioning period. In our case we learned it the hard way and had to quickly deploy an update with the new certificate. Since the update process is slow and not everyone has automatic updates enabled this causes a downtime for the users. In order to prevent this we started pinning a self-signed certificate in addition to the current server certificate. This would allow us in an emergency to use our self created CA to sign a new server certificate that is accepted in all our clients.

Got until here and would love to work with us on the next big mobile challenges? Drop us a line: jobs@applike.info

--

--