Android-SSL pinning guide

Implementation of SSL pinning in Android applications

Ulvi Jabbarli
6 min readMar 16, 2021

What is pinning?

Pinning is an optional mechanism to improve the security of a service or site that relies on SSL Certificates. Pinning allows you to associate a host with their expected cryptographic identity that should be accepted by users. Cryptographic identity is a file that can prove the identity of a server/host through cryptography.

Once a cryptography identity is known or seen for a host, it is associated or “pinned” to the host. If more than one identity is acceptable by the host, then the program holds these identities as a “pinset” and matches one of the elements in that “pinset”.

To summarize, an SSL connection tells the client to establish an encrypted connection with any identity that matches that host. Pinning tells the client a specific identity to accept when establishing a secure connection.

What should be pinned?

The first thing to decide is what should be pinned. For this choice, we have two options:

  • Certificate pinning
  • Public key pinning

If we choose public keys, we have two additional choices: pin the subjectPublicKeyInfo or pin one of the concrete types such as RSAPublicKey or DSAPublicKey.

Certificate

The certificate is the easiest to pin. You can fetch the certificate out of the band for the website. When the certificate expires, you would update your application. Assuming your application has no bugs or security defects, the application would be updated every year or two.

There is a downside to pinning a certificate. If the site rotates its certificate on a regular basis, then your application would need to be updated regularly. For example, Google rotates its certificates, so you will need to update your application about once a month (if it depended on Google services). Even though Google rotates its certificates, the underlying public keys (within the certificate) remain static.

Public Key

Public key pinning is more flexible but a little trickier due to the extra steps necessary to extract the public key from a certificate. As with a certificate, the program checks the extracted public key with its embedded copy of the public key.

There are two downsides to public key pinning:

  1. It’s harder to work with keys (versus certificates) since you usually must extract the key from the certificate. Extraction is a minor inconvenience in Java and .Net, buts it’s uncomfortable in Cocoa/CocoaTouch and OpenSSL.
  2. The key is static and may violate key rotation policies.

Hashing

Apart from the choices above, it’s also acceptable to use a hash of the information (or other transforms). In fact, the original sample programs were written using digested certificates and public keys. The samples were changed to allow a programmer to inspect the objects with tools like dumpasn1 and other ASN.1 decoders.

Hashing also provides three additional benefits:

  1. Hashing allows you to anonymize a certificate or public key. This might be important if your application is concerned about leaking information during decompilation and re-engineering.
  2. A digested certificate fingerprint is often available as a native API for many libraries, so it’s convenient to use.
  3. An organization might want to supply a reserve (or back-up) identity in case the primary identity is compromised. Hashing ensures your adversaries do not see the reserved certificate or public key in advance of its use.

Pinning in Android

There are several ways to implement Certificate Pinning in Android projects.

Since Android N, the preferred way for implementing pinning is by leveraging Android’s Network Security Configuration(NSC) feature, which lets apps customize their network security settings in a safe, declarative configuration file without modifying app code.

If devices running a version of Android that is earlier than N need to be supported, a backport of the Network Security Configuration pinning functionality is available via the TrustKit Android Library. Apart from that, TrustKit has a reporting config in which we can set a reporting URL for pin validation reports.

TrustKit Android works by extending the Android N Network Security Configuration in two ways:

  • It provides support for the <pin-set> (for SSL pinning) and <debug-overrides> the functionality of the Network Security Configuration to earlier versions of Android, down to API level 17. This allows Apps that support versions of Android earlier than N to implement SSL pinning in a way that is future-proof.
  • It adds the ability to send reports when pinning validation failed for a specific connection.

Getting SSL Pins

Before adding SSL Pinning to our App, first, we need to investigate and choose which domains and public keys need to be pinned. We will use the base64-encoded SHA-256 of a certificate’s Subject Public Key Info as an SSL Pin here which compares the hash (often called a pin) of the public key instead of comparing the entire certificate.

There are several ways to get a hashed pin from a certificate. One of them is obtaining the public key hash of your server’s certificate using a handful of OpenSSL commands. Below you can find a list of steps for that:

  • Acquire the certificate of your server. This can be done by either asking for it from your backend developer colleagues, or by simply downloading it from a browser (eg. if your server has a public website).
openssl s_client -connect your-server.com:443 -showcerts < /dev/null | openssl x509 -outform der > server_cert.der
  • When you have the certificate, you need to extract and optionally save its public key in a PEM format.
openssl x509 -inform der -in server_cert.der -pubkey -noout > server_cert_public_key.pem
  • After having the certificate, you can hash it with whatever hashing algorithm you prefer (only make sure that it is a secure algorithm). I used SHA256 to hash our key. After calculating the hash, I simply encoded it with Base64 encoding, to make it easier to store, and read.
cat server_cert_public_key.pem | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

The output of the commands listed above is the hash of your server’s public key, which can now be added to your application.

To obtain the pin for the host we are talking to we will use SSL Labs

  1. Type your-domain.com for the Hostname field and click Submit
  2. On the next page select one of the servers
  3. You will see the certificates listed; each certificate has an Additional Certificate — which is a backup. Each entry has a Pin SHA256 value. Those are the hashes of the public keys that we will add to the app.

Implementation with NSC + TrustKit in Android

  • Add TrustKit to your project’s build.gradle:
implementation ‘com.datatheorem.android.trustkit:trustkit:<version>'
  • Configuring a Pinning Policy - Deploying SSL pinning in the App requires initializing TrustKit with a pinning policy (domains, pins, and additional settings). The policy is wrapped in the official Android N Network Security Configuration i.e :
  • The path to the XML policy should then be specified in the App’s manifest in order to enable it as the App’s Network Security Configuration on Android N:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
  • Then, TrustKit should be initialized with the same path:

Once TrustKit has been initialized and the client or connection’s SSLSocketFactory has been set, it will verify the server's certificate chain against the configured pinning policy whenever an HTTPS connection is initiated. If a report URI has been configured, the App will also send reports to the specified URI whenever a pin validation failure occurred.

You can also create and register local broadcast receivers to receive the same certificate pinning error reports that would be sent to the report_uris.

Note!: TrustKit Limitations

For consumers of TrustKit’s OkHttpHelper solutions, redirects must be disabled as Pinning will currently only work properly on the initial request and not any redirects.

Network Security testing tools

There are some useful tools and guides that you can use to analyze and verify the security of networks in your app:

  • Google has a network security testing tool to help you spot cleartext traffic or other connection vulnerabilities in your app. Visit nogotofail for more info.
  • For additional security tools, check out the SafetyNet API. It includes safe browsing, integrity, and reCAPTCHA API to protect your app from spammers, phishing URLs, and other malicious traffic.
  • Lastly, there is the Android Network APIs guide which is part of the Mobile Security Testing Guide

--

--