Let's say you want to exchange some sensitive data between your application and a server. SSL should do the trick, right? Right, but that's only half of the story.
In many cases, you'll have to send sensitive data between your application and server. Take mobile banking applications for example. The last thing you want is a malicious hacker to steal someone's bank account info – or worse, their money.
Security is crucial for a mobile banking solution, so you'll be using SSL to keep that data safe and secret. But there's a catch.
What is certificate pinning?
By default, when making an SSL connection, the client checks that the server’s certificate:
- has a verifiable chain of trust back to a trusted (root) certificate
- matches the requested hostname
What it doesn't do is check if the certificate in question is a specific certificate, namely the one you know your server is using.
Relying on matching certificates between the device's trust store and the remote server opens up a security hole. The device’s trust store can easily be compromised - the user can install unsafe certificates, thus allowing potential man-in-the-middle attacks.
Certificate pinning is the solution to this problem. It means hard-coding the certificate known to be used by the server in the mobile application. The app can then ignore the device’s trust store and rely on its own, and allow only SSL connections to hosts signed with certificates stored inside the application.
This also gives a possibility of trusting a host with a self-signed certificate without the need to install additional certificates on the device.
Increased security - with pinned SSL certificates, the app is independent of the device’s trust store. Compromising the hard coded trust store in the app is not so easy - the app would need to be decompiled, changed and then recompiled again - and it can’t be signed using the same Android keystore that the original developer of the app used.
Reduced costs - SSL certificate pinning gives you the possibility to use a self-signed certificate that can be trusted. For example, you’re developing an app that uses your own API server. You can reduce the costs by using a self-signed certificate on your server (and pinning that certificate in your app) instead of paying for a certificate. Although a bit convoluted, this way, you've actually improved security and saved yourself some money.
- Less flexibility - when you do SSL certificate pinning, changing the SSL certificate is not that easy. For every SSL certificate change, you have to make an update to the app, push it to Google Play and hope the users will install it.
How does this work on Android?
Surprisingly, pinning SSL certificates on Android isn't very straightforward. We needed a solution that would work on Android 2.2 and up, so I had to do some hard digging before finding one that worked.
There are three important steps in the process:
- obtain a certificate for the desired host (preferably the whole certificate chain)
- make sure the certificate is in .bks format - this step is crucial in order for pinning to work properly across all devices
- use Apache HTTP client shipped with Android - initialize it to use the obtained .bks keystore for SSL connections
A fully functioning example that demonstrates the solution that we're using can be found here.
SSL pinning isn't something you want to do in all your applications, but for high-risk apps that require increased levels of security, it might just make sense.