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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Securing Network Data Tutorial for Android
30 mins
- Getting Started
- Understanding HTTPS
- Using Perfect Forward Secrecy
- Enforcing TLS With Network Security Configuration
- Understanding Certificate and Public Key Pinning
- Implementing Certificate Pinning
- Implementing TrustKit
- Using Certificate Transparency
- Stopping Information Leaks With OCSP Stapling
- Understanding Authentication
- Authenticating With Public-Key Cryptography
- Verifying Integrity With Elliptic-Curve Cryptography
- Verifying a Signature
- Signing a Request
- Additional Security Considerations
- Where to Go From Here?
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.
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:
On the next page, select one of the servers from the list:
You'll see there are two certificates listed; the second one is a backup. Each entry has a Pin SHA256 value:
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>
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.
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:
- Enabled Certificate Transparency.
- Added a wildcard (*) site for GitHub using +. This means you enabled Certificate Transparency on all domains that end in github.io.
- 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.