Add TrustKit certificate pinning to MobileProtect Android

TrustKit is our open-source library that makes it easy to deploy SSL public key pinning and reporting in any Android App.

Adding TrustKit pinning to an App with MobileProtect can be achieved through the following steps:

  1. Generating SSL pins for the App's server endpoints and choosing a pinning policy.

  2. Configuring the pinning policy

  3. Initializing the App with the pinning policy.

Generating SSL Pins

Before deploying SSL pinning within your App, you first need to investigate and choose which domains and public keys need to be pinned. This is very important as enabling the wrong pinning policy may prevent your App from being able to connect to its servers when the servers' keys are rotated.

The following blog post provides some information on which keys to pin and what the trade-offs are: https://noncombatant.org/2015/05/01/about-http-public-key-pinning/ .

In the context of TrustKit, an SSL pin is the base64-encoded SHA-256 of a certificate's Subject Public Key Info; this is the same as what is described in the HTTP Public Key Pinning specification.

To generate such values, a Python helper script is available within the TrustKit iOS project's repository; it can be used to generate the pin configuration from a PEM or DER certificate:

$ python get_pin_from_certificate.py ca.pem $ python get_pin_from_certificate.py --type DER ca.der

Configuring the pinning policy

To add certificate pinning with TrustKit in MobileProtect, you need to set the SSL pinning policy using network_security_config, which is wrapped in the official Android N Network Security Configuration, for example, this file contains example pins for the www.datatheorem.com domain :

<!-- res/xml/network_security_config.xml --> <?xml version="1.0" encoding="utf-8"?> <network-security-config> <!-- Pin the domain www.datatheorem.com --> <!-- Official Android N API --> <domain-config> <domain>www.datatheorem.com</domain> <!-- example pins for the datatheorem domain config --> <pin-set> <pin digest="SHA-256">u91x/rv9ivMrXO0du6N2ABI6BiiVfD0T99DTEzoqSv4=</pin> <!-- always add a backup pin --> <pin digest="SHA-256">2kOi4HdYYsvTR1sTIR7RHwlf2SescTrpza9ZrWy7poQ=</pin> </pin-set> <!-- TrustKit Android API --> <!-- Do not enforce pinning validation --> <trustkit-config enforcePinning="false"/> </domain-config> <debug-overrides> <trust-anchors> <!-- For debugging purposes, add a debug CA and override pins --> <certificates overridePins="true" src="@raw/debugca" /> </trust-anchors> </debug-overrides> </network-security-config>

Always start with pinning enforcement disabled

To avoid locking out too many users from your App when deploying SSL pinning for the first time, it is advisable to set enforcePinning to false, so that SSL connections will succeed regardless of pin validation.

Always provide at least one backup pin

In order to prevent accidentally locking users out of your site, make sure you have at least one backup pin and that you have procedures in place to transition to using the backup pin if your primary pin can no longer be used. For example, if you pin to the public key of your server's certificate, you should generate a backup key that is stored somewhere safe. If you pin to an intermediate CA or a root CA, then you should also select an alternative CA that you are willing to switch to if your current CA (or their intermediate CA) becomes invalid for some reason.

If you do not have a backup pin, you could inadvertently prevent your app from working until you released a new version of your app, and your users updated it. One such incident led to a bank having to ask their CA to issue a new certificate using a deprecated intermediate CA in order to allow their users to use the app, or face weeks of the app being unusable.

Initializing the App with the pinning policy

Then add it to the app Manifest and enable it as the App's Network Security Configuration:

<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > ... </application> </manifest>

Finally, when initializing MobileProtect, pass the network_security_config resource:

MobileProtect.init(this, R.xml.mobileprotect, R.xml.network_security_config)