Back to blog
Security 8 min

How to Implement SSL Pinning on Android

November 10, 2025

What Is SSL Pinning and Why It Matters

In a standard HTTPS connection, Android validates the server's certificate against the list of trusted Certificate Authorities (CAs) built into the operating system. This is sufficient for most use cases, but it creates an attack vector: any compromised CA — or a certificate installed by the user or an attacker — can be used to intercept traffic.

SSL Pinning addresses this by pinning the expected certificate or public key directly in the application's code. If the certificate presented by the server does not match the pinned value, the connection is rejected — even if it is technically valid according to the operating system.

This is especially critical for financial apps, healthcare apps, and any system that handles sensitive data in transit.


Types of SSL Pinning

Certificate Pinning

Pins the full certificate (.cer / .pem file). More restrictive, but requires updating the app every time the certificate is renewed.

Public Key Pinning (recommended)

Pins only the certificate's public key. Since the public key rarely changes (even across certificate renewals), this approach requires less maintenance and is considered best practice.


Implementation with OkHttp and CertificatePinner

The most common way to implement pinning on Android is via OkHttp, the standard HTTP library in the Android ecosystem.

1. Obtain the public key hash

# Extract the SHA-256 hash of the certificate's public key
openssl s_client -connect api.yourdomain.com:443 -servername api.yourdomain.com \
  </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform der \
  | openssl dgst -sha256 -binary \
  | base64

The result will look like: abc123XYZ456def789GHI012jkl345MNO678pqr901STU=

2. Configure the CertificatePinner

import okhttp3.CertificatePinner
import okhttp3.OkHttpClient

val certificatePinner = CertificatePinner.Builder()
    .add(
        "api.yourdomain.com",
        "sha256/abc123XYZ456def789GHI012jkl345MNO678pqr901STU=",
        "sha256/YOUR_BACKUP_HASH_HERE=" // always include a backup pin
    )
    .build()

val client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

3. Always include a backup pin

If the server needs to rotate its key and you have no backup pin in the app, all users running the version with the old pin will lose access until they update. Therefore:

  • Generate a backup key pair in advance
  • Include the backup public key hash in the CertificatePinner
  • Before rotating the primary key, ensure the version containing the backup pin has reached the majority of your users

Implementation via Network Security Config (Android 7+)

Another approach — without any Kotlin/Java code — is to use the network security configuration XML file:

<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <domain-config>
        <domain includeSubdomains="false">api.yourdomain.com</domain>
        <pin-set expiration="2026-12-31">
            <pin digest="SHA-256">abc123XYZ456def789GHI012jkl345MNO678pqr901STU=</pin>
            <!-- backup pin is mandatory -->
            <pin digest="SHA-256">YOUR_BACKUP_HASH_HERE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

Declare it in AndroidManifest.xml:

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

Advantage: simpler, declarative configuration.
Disadvantage: the expiration date requires an app update before it lapses.


Protection Against Bypass

SSL Pinning alone is not enough if the app can be manipulated. In high-security environments, combine it with:

  • Root Detection — check whether the device is rooted (RootBeer, SafetyNet, Play Integrity)
  • Anti-debugging — detect whether a debugger is attached
  • Code obfuscation — use ProGuard/R8 to make reverse engineering harder
  • Play Integrity API — attest device and app integrity server-side

Testing the Pin

To validate that pinning is working, use mitmproxy or Burp Suite:

  1. Install the proxy's certificate on Android as a user-trusted CA
  2. Configure the proxy on the device
  3. Attempt a request from the app — it should be rejected if the pin is correctly configured

If the request goes through, pinning is not working as expected.


Conclusion

SSL Pinning is an essential layer of defense in depth for apps that handle sensitive data. The Public Key Pinning with OkHttp approach is the most robust and maintainable option, provided you always include backup pins before any key rotation.

Combined with Play Integrity, root detection, and code obfuscation, you significantly raise the bar against traffic interception attacks.

Did you enjoy the content?

If you're building a system in this area, we can help. Talk to a specialist.

Schedule Consultation
How to Implement SSL Pinning on Android — APCosta — APCosta