SSL Pinning in Android

Anand Gaur
5 min readSep 14, 2023

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 is SSL pinning?

SSL (Secure socket layer) Certificate Pinning, or pinning for short, is the process of associating a host with its certificate or public key. Once you know a host’s certificate or public key, you pin it to that host.

In other words, you configure the app to reject all but one or a few predefined certificates or public keys. Whenever the app connects to a server, it compares the server certificate with the pinned certificate(s) or public key(s). If and only if they match, the app trusts the server and establishes the connection.

You usually add a service’s certificate or public key at development time. In other words, your mobile app should include the digital certificate or the public key within your app’s bundle. This is the preferred method since an attacker cannot taint the pin.

Why Do You Need SSL Certificate Pinning?

When the app tries to establish a connection to a server, it doesn’t determine which certificates to trust and which not to. The app relies entirely on the certificates that the iOS Trust Store provides or Android CA’s provide by Google

This method has a weakness, however: An attacker can generate a self-signed certificate and include it in the iOS/Android Trust Store or hack a root CA certificate. This allows such an attacker to set up a man-in-the-middle attack and capture the transmitted data moving to and from your app.

Restricting the set of trusted certificates through pinning prevents attackers from analyzing the functionality of the app and the way it communicates with the server.

The Importance of SSL Pinning

  1. Protection against Man-in-the-Middle Attacks: Without SSL pinning, an attacker could intercept the communication between your app and the server, posing as a middleman (MITM). They could present their own SSL certificate to your app, compromising data security. SSL pinning prevents this by ensuring that only the predefined certificates are trusted.
  2. Resilience against CA Compromises: In the traditional CA validation process, if a trusted CA’s private key is compromised, attackers can issue fraudulent certificates that your app would trust unknowingly. SSL pinning eliminates this risk since your app is not solely dependent on CAs.
  3. Enhanced Data Privacy: SSL pinning strengthens data privacy by reducing the chance of unauthorized access to sensitive information.

Types of SSL Certificate Pinning

If you want to implement pinning — which it seems you do since you’re reading this tutorial — you can decide between two options:

  • Pin the certificate: You can download the server’s certificate and bundle it into your app. At runtime, the app compares the server’s certificate to the one you’ve embedded.
  • Pin the public key: You can retrieve the certificate’s public key and include it in your code as a string. At runtime, the app compares the certificate’s public key to the one hard-coded in your code.

How to View Trusted Root Certificates on an Android Device

  1. Open Settings
  2. Tap “Security & location”
  3. Tap “Encryption & credentials”
  4. Tap “Trusted credentials.” This will display a list of all trusted certs on the device.

Getting Started with Android Network Configuration

Android Network Configuration allows developers to define network security policies for their apps using XML files. Think of it as setting the stage for a grand performance! We’ll create a file called network_security_config.xml in the res/xml directory of our Android project.

Defining Trusted Domains and Pins

Let’s start by defining our trusted domain and its corresponding public key pins in the network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set>
<!-- Pin for the SSL/TLS certificate of example.com -->
<pin digest="SHA-256">ReplaceWithYourPin</pin>
<!-- Backup Pin for the SSL/TLS certificate of example.com -->
<pin digest="SHA-256">ReplaceWithYourPin</pin>
</pin-set>
</domain-config>
</network-security-config>

The pin is a base64 encoded digest of X.509 SubjectPublicKeyInfo (SPKI). Replace ReplaceWithYourPin with base64 encoded actual SHA-256 hash of the public key or certificate for the trusted domain (e.g., example.com). You can obtain this pin by inspecting the server's SSL/TLS certificate or use this ssllabs website to get these pins.

Applying Network Configuration

Next, we need to tell our app to use this network configuration. Open the AndroidManifest.xml file and add the following attribute to the <application> element:

<application
android:networkSecurityConfig="@xml/network_security_config"
...>
...
</application>

This configures your app to use the specified network security configuration.

Now, let’s put our configuration to the TEST!

Performing HTTPS Connection with URL 💻🔒

With our network configuration in place, it’s time to unleash the power of the HttpsURLConnection class! This tech-savvy hero is our key to making secure HTTPS connections with ease.

try {
// Create a URL object for the target server
val mURL = URL("https://example.com/api/data")
with(mURL.openConnection() as HttpsURLConnection) {
requestMethod = "GET"
// Add any necessary headers here
println("URL: ${this.url}")
println("Response Code: ${this.responseCode}")
// Perform the actual connection and handle the response
val responseCode = responseCode
if (responseCode == HttpsURLConnection.HTTP_OK) {
// Hooray! Connection successful - time to celebrate!
// Now, let's process the response and show off our data handling skills! 🤓📊
} else {
// Oops! Handle other response codes (e.g., error cases) gracefully 😅
// Every superhero faces challenges - it's how we handle them that matters!
}
}
} catch (e: Throwable) {
// Uh-oh! Invalid ssl pinning or some other Network errors - but fear not, we're prepared! 💪🛡️
// Time to troubleshoot and save the day with proper error handling!
println(e)
}

With our SSL pinning and HttpsURLConnection at the helm, we can confidently navigate the digital seas, knowing that our users' data is safe and secure. So, go ahead and test your secure HTTPS connections with pride! 🔒

Remember, security is an ever-evolving adventure, and humour makes the journey all the more enjoyable. Happy testing and secure coding in Kotlin!

--

--

Anand Gaur

I am Working in Tech Mahindra as Sr. Software Engineer . I am having total 6 years of experience into Android Development