SSL Pinning on Android

SSL Pinning on Android

Banner source: https://dzone.com/refcardz/securing-mobile-applications-with-cert-pinning?chapter=1

When an Android application wants to establish a secure connection with a remote host, it checks whether the certificate's "chain of trust" is valid and compatible with the root CAs (Certificate Authorities) that are pre-installed in the device.

However, there's nothing stopping malicious actors from generating valid certificates, which allow them to intercept the communications between the app and the server - this is known as a Man in the Middle Attack.

SSL Pinning prevents this from happening by ensuring that the client only communicates using specific and trustworthy certificates.

What certificate should you pin

It's possible to pin multiple certificates from the certificate chain. The application will establish a connection with the server as long as at least one of them is valid. However:

  • pinning only a "leaf" certificate means the application will stop working once the certificate expires. This may or may not be an issue, depending on how often the application is updated (certificates are usually valid for 1-2 years);
  • pinning backup "leaves" can be a good option, but the more certificates you configure, the more certificates you need to protect from compromise;
  • pinning intermediate certificates means trusting that the intermediate CAs won't generate malicious certificates for the server. However, if all the "leaves" expire, the app will continue to work.

Also, note that typically renewing a certificate doesn't change its public key. By pinning the public key instead of the certificate itself, you can (sometimes) renew it without breaking the application.

Public Key Pinning using OkHttp

OkHttp is one of the most commonly used libraries to manage network requests in Android. Parameterising this library to use SSL pining, and more specifically, public key pinning is very simple:

  1. Build an instance of CertificatePinner that contains all the public keys and corresponding hostnames you want to pin:
val pinner = CertificatePinner
	.Builder()
    .add(CERT_HOSTNAME_1, CERT_PUBLIC_KEY_1)
    .add(CERT_HOSTNAME_2, CERT_PUBLIC_KEY_2)
    .add(CERT_HOSTNAME_N, CERT_PUBLIC_KEY_N)
	.build()

2. Pass this object as a param of the OkHttpClient builder:

val okhttpClient = OkHttpClient
	.Builder()
    .certificatePinner(pinner)
    .build()

That's it. Now your application will only establish secure connections with servers that have at least one of these certificates configured.

Sources