Securing Network Data Tutorial for Android

In this Android tutorial, you’ll learn how to keep your information private by securing network data in transit. By Kolin Stürt.

4.7 (7) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Implementing Certificate Pinning

Fortunately, this is easy to implement on Android N+. Instead of comparing the entire certificate, it compares the hash (more on this later) of the public key, often called a pin.

Certificate pinning

To get the pin for the host you're talking to, head to SSL Labs. Type github.io for the Hostname field and click Submit:

SSL Server Test page on the SSL Labs site

On the next page, select one of the servers from the list:

List of github.io servers

You'll see there are two certificates listed; the second one is a backup. Each entry has a Pin SHA256 value:

Certificates with their Pin SHA256 section highlighted

Note: These values may change over time, so be sure to lookup these values before using them

Those are the hashes of the public keys that you'll add into the app. Go back into network_security_config.xml and add them right after the domain tag for github.io:

<pin-set>
  <pin digest="SHA-256">xlDAST56PmiT3SR0WdFOR3dghwJrQ8yXx6JLSqTIRpk=</pin>
  <pin digest="SHA-256">k2v657xBsOVe1PQRwOsHsw3bsGT2VzIqz5K+59sNQws=</pin>
</pin-set>
Note: There are many ways to get the public key hash. One alternative is to download the certificate directly from the website and run OpenSSL commands on it. If you're developing an app for a company, you might bug IT for it directly. :]

With that you've added certificate pinning support for Android N and higher, but what if your app needs to support versions under N? You will handle this case next by using TrustKit.

Implementing TrustKit

TrustKit is a library that uses the same format in network_security_config.xml to add support for versions under Android N.

You'll now add the TrustKit library to the project. Head to your app module build.gradle and add this to your list of dependencies:

implementation "com.datatheorem.android.trustkit:trustkit:$trustkit_version"

Next, add the TrustKit version to your project level build.gradle file at the beginning of the buildscript block:

ext.trustkit_version = '1.1.2'

Be sure to sync your Gradle files before proceeding.

Then, in network_security_config.xml, add the following right after the pin-set section:

<trustkit-config enforcePinning="true" />

This tells TrustKit to enable certificate pinning using the existing pins you added above. You need to initialize TrustKit with that security configuration somewhere near your app startup, before you make any network requests.

In MainActivity.kt, add the initialization code to onCreate(), just before the last line that sets petRequester (import TrustKit when required):

TrustKit.initializeWithNetworkSecurityConfiguration(this)

Now, go back and tell HttpsURLConnection to involve TrustKit when making a connection. In PetRequester.kt, add the following right after connection declaration line:

connection.sslSocketFactory = TrustKit.getInstance().getSSLSocketFactory(connection.url.host)

HttpsURLConnection will now use the TrustKit socket factory, which will make sure the certificates match.

If you build and run the app, you will see no change.

To test that everything is working, navigate to network_security_config.xml. Change any character other than = for each of the pin digest entries. Here's an example:

<pin digest="SHA-256">klDAST56PmiT3SR0WdFOR3dghwJrQ8yXx6JLSqTIRpk=</pin>
<pin digest="SHA-256">m2v657xBsOVe1PQRwOsHsw3bsGT2VzIqz5K+59sNQws=</pin>

If you build and run, you'll see an error that says either com.datatheorem.android.trustkit.reporting.BackgroundReporter.pinValidationFailed or javax.net.ssl.SSLHandshakeException: Pin verification failed.

Pin verification failed error

You just successfully added certificate pinning to your app! Don't forget to undo those changes that caused pin verification to fail. :]

For more information about certificate pinning in general, see the OWASP documentation.

While pinning is popular, some companies don't like that they have to update their apps from time to time with new pins. That's a problem that Certificate Transparency solves.

Using Certificate Transparency

Certificate Transparency is a new standard that audits the certificates presented during the setup of an HTTPS connection without requiring hard-coded values in the app.

When a CA issues a certificate, it must submit it to a number of append-only certificate logs. Certificate Transparency has nearly real-time monitoring to determine if someone has compromised the CA or if the CA issued the certificate maliciously.

The owner of the domain can scrutinize the entries and your app cross-checks the logs. The certificate is only valid if it exists in at least two logs.

When an entity revokes a certificate in a security situation, you want to know about it immediately. You can use Certificate Transparency on top of pinning for greater security. You'll add it to your app next.

In the app module build.gradle file, add the following to the list of dependencies and sync Gradle:

implementation 'com.babylon.certificatetransparency:certificatetransparency-android:0.2.0'

Next, navigate to PetRequester.kt file. In retrievePets, find the line that declares connection. Add the following right under that line (import certificateTransparencyHostnameVerifier when required):

connection.hostnameVerifier = // 1
    certificateTransparencyHostnameVerifier(connection.hostnameVerifier) {
      // Enable for the provided hosts
      +"*.github.io" // 2

      // Exclude specific hosts
      //-"kolinsturt.github.io" // 3
    }

Here, you:

  1. Enabled Certificate Transparency.
  2. Added a wildcard (*) site for GitHub using +. This means you enabled Certificate Transparency on all domains that end in github.io.
  3. Excluded specific domains using -. This example allows all GitHub domains except the one starting with kolinsturt.

You should be able to build and run the app without any issue.

But we are not done yet. Next, you'll learn about a few more options that affect certificate checking.

Stopping Information Leaks With OCSP Stapling

The traditional way to determine if an entity revoked a certificate is to check a Certificate Revocation List (CRL). To do this, your app must contact a third party to confirm the validity of the certificate, which adds network overhead. It also leaks private information about the sites you want to connect with to the third party.

Here Online Certificate Status Protocol (OCSP) Stapling comes to the rescue. When you start an HTTPS request to the server using this method, the validity of the server's certificate is already "stapled" to the response.

OCSP Stapling is enabled by default, but you can disable it or customize the behavior of certificate revocation using PKIXRevocationChecker.Option. See commented code inside the PetRequester.kt's init block in the final project for sample code.

The server you're connecting to can't forge this info. The CA signs that info ahead of time, so it doesn't know which site you want to access.

What is signing, you ask? It's a way to verify the integrity of data. Even though you've encrypted data, how do you know it's authentic in the first place? You'll now use authentication to ensure the integrity of the information you send and receive over the network.