By the TrustPin engineering team · Published 14 September 2026 · About 12 minutes
The short answer
Certificate pinning is a security technique in which an application accepts a TLS connection only if the server presents a certificate, or a public key, that matches a pin the application already trusts. It adds a check on top of normal certificate validation, so a certificate issued by a compromised or coerced certificate authority, or by an interception proxy whose root the device trusts, is rejected even though the operating system would accept it. The price is operational: a pin that is not updated before the server's key changes locks every user out.
Why does TLS need an extra check?
When an app opens an HTTPS connection, the operating system checks that the server's certificate chains up to one of the root certificate authorities in its trust store, that the hostname matches, and that the dates are valid. That store holds well over a hundred roots, and every one of them can issue a certificate for any name on the internet. The system has no way to know that your API's certificate is supposed to come from one specific authority and one specific key.
That gap has been exploited repeatedly. DigiNotar issued fraudulent certificates for Google domains in 2011 and went bankrupt weeks later; Comodo issued nine rogue certificates the same year; TURKTRUST mistakenly issued intermediate certificates in 2013; and in 2018 browsers stopped trusting the entire Symantec hierarchy after years of mis-issuance. Certificate Transparency, which now requires publicly trusted certificates to be logged, makes mis-issuance discoverable after the fact, but it does not stop a connection while it is happening.
Mobile apps face a second, more mundane version of the problem: a device on public Wi-Fi behind a malicious proxy, or a device whose owner installed an interception root to inspect traffic. Both present a certificate the device trusts. Pinning is the check that says: trusted is not enough, it has to be ours.
How does certificate pinning work?
The app holds a set of pins for each host it talks to. A pin is a cryptographic hash, almost always SHA-256, of either the whole certificate or of its Subject Public Key Info, the DER-encoded public key and algorithm identifier. During the TLS handshake, after the normal validation succeeds, the app hashes what the server presented and compares it against the pins for that hostname. If nothing matches, the connection is closed before a single byte of application data is sent.
- The app obtains a pin set: compiled into the binary, read from a configuration file, or fetched from a signed source.
- On each new connection the platform validates the chain as usual.
- The app computes the hash of the leaf certificate or of a key in the chain.
- The hash is compared against the pins for that host; a match anywhere in the chain is usually accepted.
- No match: the handshake is cancelled and the request fails. A match: the request proceeds.
Every platform offers a hook at exactly this point: a URLSessionDelegate challenge on iOS, OkHttp's CertificatePinner or the Network Security Config on Android, a SecurityContext in Flutter, and native modules in React Native. The platform guides show each one with code.
Two rules turn a pinning check into a pinning strategy. Keep a backup pin for the key you will rotate to next, so the switch does not lock anyone out. And decide what happens when the pin set is stale: fail closed (refuse the connection, which is the point of pinning) or fail open (stop pinning, which quietly removes the protection). Android's Network Security Config, for example, disables pinning entirely after the expiration date you set.
What should you pin: the certificate or the public key?
A leaf certificate pin hashes the entire X.509 certificate: public key, serial number, validity dates, extensions. It changes on every renewal, even when the key does not, so it must be updated at least as often as the certificate. It is the strictest option and the hardest to operate.
An SPKI pin hashes only the Subject Public Key Info. It survives any renewal that reuses the same key pair, it is what OkHttp, Android's Network Security Config, Apple's NSPinnedDomains and TrustKit all use, and it is the format OWASP recommends. Computing one takes a single OpenSSL pipeline:
openssl x509 -in cert.pem -pubkey -noout \
| openssl pkey -pubin -outform der \
| openssl dgst -sha256 -binary \
| base64
# → YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=The catch is that key reuse is the exception, not the rule. AWS Certificate Manager always generates a new key pair at renewal. Let's Encrypt's certbot does too unless you pass --reuse-key. Cloudflare's managed certificates rotate keys. So in practice an SPKI pin changes about as often as a leaf pin; what it buys you is a stable identity for the next key, which you can publish as a backup pin before the switch.
Pinning an intermediate or a root CA instead survives more renewals, but it trusts every certificate that CA ever issues for your hostname, which is close to the situation pinning was meant to narrow. If you go that way, pin the CA and keep the ability to change it: CAs re-key too.
What is the difference between static and dynamic pinning?
Static pinning compiles the pin set into the app. Changing a pin means building a new version, passing store review, and waiting for users to install it. The pins are as trustworthy as the binary, which is their strength, and as slow to change as the release train, which is their weakness.
Dynamic pinning keeps the pin set outside the binary and lets the app fetch a new one at runtime. Done carelessly, this is worse than no pinning at all: an app that downloads its pins over the same connection it is trying to protect, without a signature, will happily accept a pin set handed to it by the attacker. HTTP Public Key Pinning for browsers had exactly this trust-on-first-use flaw among others, and every major browser had dropped it by 2019.
Done properly, dynamic pinning means: the app ships with a verification key, the pin set is signed with the matching private key that never leaves the operator, the device verifies the signature before trusting a single pin, older configurations cannot replace newer ones, and the last verified configuration is kept on the device so that a delivery outage never turns into a pinning outage. With those properties the pin set can change in minutes while the trust anchor stays as fixed as it was in the static case.
Why is certificate rotation the real risk?
Most pinning incidents are not attacks. They are apps that stopped working because a certificate renewed with a new key and the pin set did not follow. The certificate industry is now making that failure mode routine. Under the CA/Browser Forum's ballot SC-081, the maximum lifetime of a public TLS certificate falls to 200 days in March 2026, 100 days in March 2027, and 47 days in March 2029. Let's Encrypt certificates already last 90 days, with six-day certificates available.
Put that next to the mobile release cycle. A store review takes a day or two. Most users update within a few weeks. A long tail never updates at all. With 47-day certificates and a new key at each renewal, a static pin set will expire faster than the app can be updated, and the backup pin only buys one extra cycle. Teams that tried to run static pinning on this schedule end up with one of two outcomes: a remote kill switch that disables pinning at the first sign of trouble, or a decision to remove pinning altogether. Both are how "pinning is dead" became a common claim.
The arithmetic changes only when the pin set can be updated on the same timescale as the certificates: a renewed certificate appears in Certificate Transparency, its key is published to installed apps as a backup pin, the server switches, and the old pin ages out. That is a job for a scheduled pipeline, not for a release manager.
What does OWASP say about pinning?
OWASP's community page on certificate and public key pinning now opens by weighing the risk of CA compromise against the risk of self-inflicted downtime, and concludes that for most applications pinning is not recommended. Its Pinning Cheat Sheet keeps the door open: pin when you control both ends of the connection, when you operate in a hostile environment, and when you can update the pin set securely without shipping a new app. The Mobile Application Security Verification Standard lists identity pinning as a control for apps that need defense in depth (MASVS-NETWORK-2), and the testing guide checks that pinned apps fail closed and keep backup pins.
Read together, the guidance is consistent: the objection is to unmanaged pinning. A secure update mechanism is not an optional extra; it is the precondition. We answer the four specific implementation problems OWASP documented on the research page.
When is certificate pinning worth it?
Pin when the app carries credentials, payments, health or financial data, when it is used on networks you do not control, when its API is a target for bots and credential stuffing, or when a regulator or a customer's security review expects it. In those cases the cost of interception is high and the users are exactly the ones on hostile networks.
Do not pin in a browser (the mechanism no longer exists), against hosts whose keys you do not control and cannot observe, or without a rotation plan. And do not mistake pinning for the whole picture: Certificate Transparency monitoring, CAA records, short-lived access tokens and mutual TLS solve adjacent problems and combine well with it.
How do you implement pinning on each platform?
| Platform | Built-in mechanism | Rotation without a release | TrustPin SDK docs |
|---|---|---|---|
| iOS, macOS | NSPinnedDomains in Info.plist (iOS 14+), or a URLSessionDelegate challenge handler | No | iOS and macOS SDK |
| Android | Network Security Config <pin-set> (API 24+), OkHttp CertificatePinner | No | Android SDK |
| Flutter | SecurityContext with bundled certificates, badCertificateCallback | No | Flutter SDK |
| React Native | None in JavaScript; native modules or community packages | No | React Native SDK |
Every built-in mechanism shares the same property: the pins live in the binary. That is the gap a dynamic approach fills, on every platform, with the same signed configuration.
Where does TrustPin fit?
TrustPin is dynamic pinning as a service. The app ships with your project's public key; the pin set is a configuration signed with ECDSA P-256 and verified on the device before any pin is trusted; older configurations cannot replace newer ones; the last verified configuration is kept on the device, and a signed copy can be embedded for a first launch offline. The CLI's projects refresh-certs command reads the live certificate and the Certificate Transparency logs for a domain and stages every unexpired key as a pin, so the next certificate is pinned before the server starts using it. Bring Your Own Keys lets you sign with a key TrustPin never sees.
The free plan covers one project and two domains with no time limit; the developer documentation goes deeper into hash algorithms, pin types and the SDK behaviour on each platform.
Frequently asked questions
Does certificate pinning replace normal TLS validation?
No. Pinning is an extra check that runs after the operating system has validated the certificate chain, the hostname and the validity dates. A pinned connection has to pass both: a valid chain that ends in a pinned key, or a pinned certificate. Pinning never makes an invalid certificate acceptable.
Can certificate pinning be bypassed?
On a device the attacker controls, yes: a rooted or jailbroken phone running a hooking framework can patch the check out of the app. Pinning protects your users from attackers on the network; it does not protect the app from its own user or from malware on the device. Runtime application self-protection (RASP) covers that second threat, which is why TrustPin partners with Promon.
Should I pin the leaf certificate, the intermediate CA, or the root?
Pin the public key (SPKI hash) of the leaf if you control the server's key, and keep a backup pin for the next key. Pinning an intermediate or root CA survives more renewals but trusts every certificate that CA ever issues for your name, which is exactly what pinning was meant to narrow. Whatever you pin, make sure you can change it without a release.
What happens when a pinned certificate expires or rotates?
If the new certificate uses a new key and the app has no pin for it, every connection to that host fails until the app receives a new pin set. With static pinning that means an app-store release and waiting for users to update. With dynamic pinning the new pin is published to installed apps in minutes, ideally before the server switches certificates.
Is certificate pinning still recommended now that certificates last 47 days?
Only with a way to update pins remotely. Public TLS certificates drop to a 200-day maximum in March 2026, 100 days in March 2027 and 47 days in March 2029, and most managed certificate services generate a new key at every renewal. A static pin set cannot keep up with that. Pinning remains one of the strongest defenses against network interception for mobile apps, provided the pin set is updated dynamically and verified cryptographically on the device.
Further reading
- TrustPin docs: iOS and macOS integration
- TrustPin docs: Android and JVM integration
- TrustPin docs: Flutter integration
- TrustPin docs: React Native integration
- TrustPin docs: CLI integration
- Research: the OWASP pinning challenges answered
- Certificate pin lookup tool: SPKI pins for any hostname
- TrustPin docs: certificate pinning, hash algorithms and pin types
- OWASP Pinning Cheat Sheet
- CA/Browser Forum ballot SC-081v3: the certificate lifetime schedule
