SSL Pining
SSL Pinning in Android
> Correct technical name: Certificate Pinning. It is commonly called SSL pinning, although modern Android connections use TLS.
1. What is SSL Pinning?
Normally, Android trusts a server certificate when it is signed by any trusted Certificate Authority (CA).
Certificate pinning adds one more check:
> The server certificate or public key must match a value already stored inside the application.
flowchart TD
A["Android App"] --> S["Server"]
S --> C["Server sends certificate"]
C --> V{"Normal TLS validation valid?"}
V -->|No| R["Reject connection"]
V -->|Yes| P{"Pin matches?"}
P -->|Yes| E["Create secure connection"]
P -->|No| RPinning protects highly sensitive connections against some compromised or incorrectly trusted CAs.
---
2. Normal HTTPS vs Certificate Pinning
Normal HTTPS
flowchart LR
A["Application"] --> C["Certificate"]
C --> CA["Trusted CA"]
CA --> OK["Connection allowed"]The application trusts a certificate signed by any CA trusted by the device.
HTTPS with pinning
flowchart LR
A["Application"] --> C["Certificate"]
C --> CA["Normal CA validation"]
CA --> P["Public-key pin validation"]
P --> OK["Connection allowed"]Both validations must succeed:
1. Normal TLS certificate validation
2. Application pin validation
---
3. Why Do We Need Pinning?
Suppose an attacker tries a man-in-the-middle attack:
flowchart LR
A["Application"] --> M["Attacker"]
M --> S["Real server"]If the attacker presents another certificate:
* Normal HTTPS checks may reject it.
* If a compromised or user-installed CA makes it otherwise trusted, a correctly configured pin can still reject it.
* The public key does not match the application’s stored pins.
Common use cases include:
* Banking applications
* Payment applications
* Healthcare applications
* Applications handling sensitive enterprise data
Pinning should be a risk-based decision because incorrect rotation can block every pinned API request.
---
4. What Can Be Pinned?
| Pin type | Meaning | Main problem |
|---|---|---|
| Whole certificate | Pin exact certificate | Breaks when certificate is renewed |
| Public key | Pin certificate’s public key hash | Survives renewal if the same key remains |
| Intermediate CA | Trust a specific intermediate authority | Trusts other certificates from that authority |
| Root CA | Trust a root authority | Very broad trust; usually not preferred |
A common Android approach is to pin the SHA-256 hash of the certificate’s Subject Public Key Info (SPKI).
sha256/Base64EncodedPublicKeyHashOkHttp’s CertificatePinner uses SPKI public-key hashes. OkHttp CertificatePinner
---
5. Pinning with OkHttp
val certificatePinner = CertificatePinner.Builder()
.add(
"api.example.com",
"sha256/PRIMARY_PUBLIC_KEY_HASH="
)
.add(
"api.example.com",
"sha256/BACKUP_PUBLIC_KEY_HASH="
)
.build()
val okHttpClient = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()Retrofit:
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(
GsonConverterFactory.create()
)
.build()Now every HTTPS request to api.example.com must pass both normal TLS verification and pin verification.
---
6. Multiple Domains
Add pins separately for each protected hostname:
val certificatePinner = CertificatePinner.Builder()
.add(
"api.example.com",
"sha256/API_PRIMARY_PIN="
)
.add(
"api.example.com",
"sha256/API_BACKUP_PIN="
)
.add(
"payment.example.com",
"sha256/PAYMENT_PRIMARY_PIN="
)
.add(
"payment.example.com",
"sha256/PAYMENT_BACKUP_PIN="
)
.build()A pin for one hostname does not automatically protect every other domain.
Wildcard hostname
.add(
"*.example.com",
"sha256/PUBLIC_KEY_HASH="
)Use wildcards carefully. Confirm exactly which hosts and certificate chains are expected.
---
7. Primary and Backup Pins
Never depend on only one pin.
flowchart TD
C["Server certificate chain"] --> P{"Matches any valid pin?"}
P -->|Primary pin| A["Allow"]
P -->|Backup pin| A
P -->|No match| R["Reject"]Example:
.add(
"api.example.com",
"sha256/CURRENT_KEY_HASH="
)
.add(
"api.example.com",
"sha256/NEXT_KEY_HASH="
)The backup pin should belong to a separately controlled key that can be activated during certificate rotation or emergency recovery.
OWASP recommends using a pin set and planning backup/rotation carefully because a broken pin can create a complete service outage. OWASP Pinning Guidance
---
8. Certificate Rotation
Certificates expire and must be renewed. A safe rotation plan is essential.
Safe rotation process
flowchart TD
A["App contains current and future pins"] --> B["Release app update"]
B --> C["Wait for adoption"]
C --> D["Server switches to new key"]
D --> E["Both old and new apps continue working"]
E --> F["Remove expired pin in a later release"]Recommended process
1. Generate the future server key.
2. Add its public-key hash as a backup pin.
3. Release the application update.
4. Allow users time to update.
5. Rotate the server certificate/key.
6. Keep old and new pins during the transition.
7. Remove the old pin in a later application version.
Dangerous process
Server changes key first
↓
Installed apps contain only old pin
↓
All API calls fail
↓
Application cannot recover remotelyThis is called bricking the network connection.
---
9. Finding the Public-Key Pin
OpenSSL can calculate an SPKI SHA-256 hash:
openssl s_client -connect api.example.com:443 \
-servername api.example.com </dev/null 2>/dev/null \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform der \
| openssl dgst -sha256 -binary \
| openssl base64Result format:
AbCdEf123...=Use it as:
sha256/AbCdEf123...=Important precautions:
* Run the command on a trusted network.
* Confirm the value through an approved independent source.
* Check the complete production certificate chain.
* Coordinate with the backend/security team.
* Never copy an unverified hash from a random failure message.
---
10. Network Security Configuration
Android also supports pinning through XML Network Security Configuration.
res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">
api.example.com
</domain>
<pin-set expiration="2027-12-31">
<pin digest="SHA-256">
PRIMARY_PUBLIC_KEY_HASH
</pin>
<pin digest="SHA-256">
BACKUP_PUBLIC_KEY_HASH
</pin>
</pin-set>
</domain-config>
</network-security-config>Register it in the manifest:
<application
android:networkSecurityConfig=
"@xml/network_security_config"
android:usesCleartextTraffic="false">
</application>OkHttp vs Network Security Config
OkHttp CertificatePinner | Network Security Config |
|---|---|
| Implemented in Kotlin | Implemented in XML |
| Applies to configured OkHttp client | Applies through Android network-security policy |
| Easy for Retrofit/OkHttp | Central Android configuration |
| Pin rotation requires app update | Pin rotation also normally requires app update |
| Works at HTTP client level | Works at platform trust configuration level |
Choose one controlled approach based on project architecture and security policy. Avoid inconsistent duplicate configurations.
---
11. Internal Working
When the application sends an API request:
sequenceDiagram
participant A as Android App
participant S as Server
participant TLS as TLS Validator
participant P as Pin Validator
A->>S: HTTPS connection request
S-->>A: Certificate chain
A->>TLS: Validate dates, hostname and CA chain
TLS-->>A: Standard validation passed
A->>P: Hash public keys in certificate chain
P->>P: Compare with configured pin set
P-->>A: Pin matched
A->>S: Continue encrypted connectionIf no configured pin matches, OkHttp throws an `SSLPeerUnverifiedException`.try {
api.getProducts()
} catch (exception: SSLPeerUnverifiedException) {
// Report a secure connection failure
}Do not silently bypass pinning after a failure.
---
12. Error Handling
A pinning failure should normally be treated as a secure-connection failure.
User-friendly message:
A secure connection could not be established.
Please update the application or try again later.Internally record safe diagnostic information such as:
* App version
* Hostname
* Failure type
* Time
* Network category
* Certificate-chain metadata allowed by policy
Do not log:
* Access tokens
* Authorization headers
* Passwords
* OTPs
* Sensitive request/response bodies
---
13. Debug and Security Testing
Charles Proxy, Fiddler or corporate TLS inspection may fail against a pinned production configuration because they present a different certificate.
A controlled debug build can use different security behaviour when explicitly approved.
val clientBuilder = OkHttpClient.Builder()
if (!BuildConfig.DEBUG) {
clientBuilder.certificatePinner(
productionCertificatePinner
)
}Important:
* Do not accidentally disable pinning in release builds.
* Verify the release artifact.
* Keep debug trust configuration separate.
* Do not ship debugging proxy certificates.
* Test production pinning on a controlled device and network.
OWASP notes that separate debug and release variants can support security testing, provided the production build retains its protections. OWASP Mobile Security Testing
---
14. What Pinning Protects Against
Pinning can reduce risk from:
* Compromised CA
* Incorrectly issued certificate
* Malicious user-installed CA
* Some TLS interception proxies
* Some man-in-the-middle scenarios
---
15. What Pinning Does Not Protect Against
Pinning does not protect against:
* Compromised backend server
* Stolen server private key matching the pin
* Vulnerabilities in application logic
* Malware controlling the device
* Runtime hooking on a compromised/rooted device
* An attacker modifying the application and disabling the check
* Insecure token storage
* Sensitive-data logging
OWASP explicitly warns that pinning can be bypassed when the client device or application process is compromised. OWASP Certificate and Public-Key Pinning
Pinning is defence in depth, not complete security.
---
16. Common Mistakes
Mistake 1: Using only one pin
If that key changes, every network call fails.Use a primary and backup pin.
Mistake 2: Pinning an exact certificate without planning renewal
A renewed certificate may have a different certificate hash.
Public-key pinning offers more flexibility when the same key is retained, but key rotation must still be planned.Mistake 3: Trusting all certificates
Never use a TrustManager that accepts everything:
override fun checkServerTrusted(
chain: Array<X509Certificate>,
authType: String
) {
// Accept everything — insecure
}This disables certificate validation.
Mistake 4: Disabling hostname verification
Never do this:
hostnameVerifier { _, _ -> true }A valid certificate for another hostname must not be accepted.
Mistake 5: Bypassing pinning after failure
Do not retry the request using an unpinned client.
That removes the security protection exactly when a suspicious connection occurs.
Mistake 6: No rotation plan
Backend, mobile and security teams must coordinate:
* Current pin
* Backup pin
* Rotation date
* App adoption period
* Emergency recovery procedure
Mistake 7: Pinning third-party services without control
A third party may change certificates or keys without coordinating with your mobile release schedule.
Pin only where your organization can operate the certificate lifecycle reliably.
Mistake 8: Assuming pinning is always recommended
Pinning adds security only when its operational risks are controlled. For many applications, correct HTTPS validation and secure network configuration may be safer than poorly managed pinning.
---
17. Testing Strategy
Test at least these cases:
| Scenario | Expected result |
|---|---|
| Correct primary pin | Connection succeeds |
| Correct backup pin | Connection succeeds |
| Incorrect pin | Connection fails |
| Expired certificate | Connection fails |
| Wrong hostname | Connection fails |
| Untrusted CA | Connection fails |
| Certificate/key rotation | Supported app versions continue working |
| TLS interception proxy | Production connection fails |
| Debug security setup | Works only in approved debug build |
Testing failure is as important as testing success.
---
18. Interview Questions and Answers
Q1. What is SSL pinning?
SSL pinning, more accurately certificate pinning, restricts an application to predefined server certificate or public-key identities in addition to normal TLS validation.
Q2. How does it prevent MITM attacks?
Even if an attacker presents a certificate that the device otherwise trusts, the connection is rejected when its public-key hash does not match the application’s pins.
Q3. Certificate pinning vs public-key pinning?
Certificate pinning pins the entire certificate and usually breaks when the certificate changes. Public-key pinning pins the public-key hash and can survive certificate renewal when the same key is reused.
Q4. Why do we need a backup pin?
A backup pin allows the backend to rotate to a pre-approved key without breaking installed application versions.
Q5. Where are pins stored?
Pins are packaged inside application code or Network Security Configuration. Changing them normally requires an application update.
Q6. What happens when pin validation fails in OkHttp?
OkHttp rejects the connection and throws SSLPeerUnverifiedException.
Q7. Should the application fall back to normal HTTPS after pin failure?
No. Falling back would bypass the protection and could permit the suspected connection.
Q8. How do you rotate certificates safely?
Ship the future public-key pin as a backup, wait for sufficient app adoption, rotate the server key and later remove the old pin.
Q9. Can pinning be bypassed?
Yes, on a compromised device an attacker may hook or modify application code. Pinning is defence in depth, not an absolute guarantee.
Q10. What are the risks of pinning?
Incorrect pins, certificate changes or poor rotation planning can block all API communication for installed application versions.
Q11. Should we pin third-party APIs?
Only with caution. If the third party changes its certificate or public key without coordination, the application can stop working.
Q12. How would you implement it with Retrofit?
Configure an OkHttp CertificatePinner with primary and backup SHA-256 SPKI hashes, attach the OkHttp client to Retrofit and never use an insecure fallback client.
---
Final Interview Answer
> Certificate pinning adds an extra identity check on top of normal HTTPS. During the TLS handshake, Android first validates the certificate chain, expiry and hostname. OkHttp then compares the SHA-256 public-key hash from the server’s certificate chain with the pins packaged in the application. If any approved pin matches, the connection continues; otherwise, it fails with `SSLPeerUnverifiedException`. I always configure primary and backup pins, coordinate certificate rotation with the backend team, test failure scenarios and never fall back to an unpinned connection. Pinning is defence in depth and must be used only when the organization can manage its operational risks.