Recently at Mobiconf in Kraków I saw Marcos Placona give an excellent talk about making apps more secure. One of the subjects that he covered was that of certificate pinning which can really help to secure your API calls to your server. In this short series we’ll take a look at what certificate pinning is, why it can be a good thing, some pitfalls and, of course, how to implement it on Android.
Previously we looked at how SSL encryption works and found a couple of use-cases where we may be vulnerable to man-in-the-middle attacks: Firstly in cases where our server uses a self-signed key, and secondly where an attacker may be able to obtain a certificate fraudulently identifying their server as ours. While we already looked at how a self-signed certificate could be made more secure by adding the root certificate as a trusted authority, there is another way that we could tighten the security of both of these cases, and that is by using Certificate Pinning.
This is actually a really simple concept, Alice has some a mechanism to verify that during the initial exchange of public keys, the certificate being offered really is Bob’s certificate. This is achieved by Alice having details of Bob’s certificate ahead of this key exchange and during the key exchange Alice is able to verify that the certificate that Bob offers to verify his public key matches the details that Alice already holds. if Charles were to try an initiate a man-in-the-middle attack using his own certificate, then this validation by Alice would fail because Alice would know that the certificate and public key being offered didn’t match the details that she already holds. In app terms, this is done by having a hash which identifies Bob’s certificate embedded within the app, and this is known as certificate pinning.
Certificate Pinning is useful in cases where we own both endpoints – i.e. both the app itself and the server. It enables us to ensure that we only connect to a server which uses a specific certificate.
There is, however, one major risk with Certificate Pinning: What happens if we need to change Bob’s certificate. Thos may be for a number of reasons: The current certificate may be expiring; there may be a change to the specifics of the server which require a new certificate; or there may be a need to switch to a different Certificate Authority. Any of these could require a new certificate being used on the server, and if the certificate is changed, then the new certificate will no longer match the pinned one, so the communications will fail. This is really bad.
The most important thing to do when considering using certificate pinning is to talk to the people responsible for maintaining the server endpoint. Let them know that you are considering using certificate pinning in the app, and listen to what they have to say. Ultimately it will be them that makes changes to the server config, so they may have valid reasons why certificate pinning would be a bad idea.
That said, there are some mitigating strategies that we can use when implementing certificate pinning. The most important of which is to have backups included in our pinned certificates. While this may sound like we should have multiple certificates which we can switch between, it is actually a bit cleverer than that. Previously we looked at the trust chain which exists on a certificate which is signed by a trusted CA. This means that the certificate may be signed either an intermediate certificate, or a root certificate. An intermediate certificate may be signed either another intermediate certificate, or a root certificate. But the final certificate in this chain must be a root certificate. It is this trust chain which can provide us with a mechanism which may assist us in cases where the server certificate needs to change. We can add any or all of the certificates in this trust chain to our list of pinned certificates. This means that even if the actual server certificate changes, the pinning will still work provided at least one of the pinned certificates exists in the trust chain of the new certificate. In other words, if we obtain a new certificate through the same CA as the old one, and we have pinned all of the certificates in the trust chain as well as the old certificate, then the new certificate will work.
This will only work if we use the same CA to generate the new certificate. If we are changing the certificate provider then we’ll need a little more in the way of planning. We’ll actually need details of the new certificate in advance, and create a new build of the app which has the new certificate and trust chain pinned as well as the old certificate and trust chain. We then release this new version before the change is made on the server. This version of the app will work with both old and new certificates. Once the server switch has been made, then the old pinned certificate and trust chain can be removed from subsequent releases of the app. Having to plan this change carefully means working closely with those responsible for the server, and why they really need to know if you are using certificate pinning within the app – so that they know the potential repercussions of changing certificates without proper planning.
There is another potential gotcha when implementing certificate pinning on Android and that is to be very careful about how you perform the trust chain validation. Some of the Java APIs do not actually return the trusted certificate path, but simply a list of certificates provided by the server which could be a malicious server providing the list we expect rather that the actual trust chain. A full explanation of this is a bit beyond the scope of this article, but there is a full, detailed explanation here.
That said, certificate pinning is actually really easy to implement in a secure way if we use the correct tools, and in the final article in this series we’ll turn our attention to code and see how we can easily implement certificate pinning within our Android apps.
© 2018, Mark Allison. All rights reserved.
Copyright © 2018 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.