FIELD NOTES ON BUILDING SYSTEMS

A personal technical publication

SSL Pinning Explained: How Certificate and Public Key Pinning Work

Learn how SSL pinning works through a simple attack story, with Android and iOS guidance, certificate renewal, benefits, and risks.

SSL pinning

SSL pinning adds an extra check to an app’s HTTPS connection: the server must present the certificate or public-key information the app expects, as well as pass the normal security checks. Maya’s photo-printing app shows why this matters.

Maya is making a family album. Her pictures are stored in a cloud photo account, and she uses a printing app on her phone to choose which ones to print.

She connects her account, opens the album, and starts browsing the thumbnails. The app requests her pictures over HTTPS.

HTTPS protects the connection using Transport Layer Security, or TLS, the modern replacement for SSL. “SSL pinning” remains the familiar name for the extra check we will explain. [From SSL to TLS]

We will follow that request three ways: first to the real photo service, then through an attacker’s server, and finally with an extra check that stops the impersonation. That check is called SSL pinning.

Maya and the photo service are fictional. The story illustrates a certificate-trust failure that has occurred in real attacks.

First, the app reaches the real photo service

The app is configured with the full URL for its photo request. In our example, that URL is https://api.photos.example/photos.

The URL has several parts. https tells the app to use HTTPS. api.photos.example is the hostname. /photos is the path for the photo request. Together, they give the app the address to call. [URL components]

To connect, the app needs an IP address for that hostname. This is the network address of the server it will contact. It looks up the hostname through DNS. DNS uses the hostname, not the full URL or its path. [DNS names and addresses]

For this visit, DNS returns the real photo service’s IP address.

Hostname the app wants: api.photos.example
DNS answer:            the real photo service’s IP address
Connection reaches:    the real photo service

The app checks the server’s certificate

The app connects to that server. In this walkthrough, each visit includes a new certificate check before any photo request is sent. The server shows a certificate. Think of this as the server’s identity document. It lists the server’s names and public key. A certificate authority, or CA, signs it.

The app checks the certificate before sending the request:

  1. Can we trust who issued it? The app verifies the signatures that link the certificate to a CA it trusts. Its trusted CA certificates are kept in the device or app’s trust store. [Certificate verification]

  2. Is it valid for this hostname? The app checks api.photos.example against the names in the certificate’s Subject Alternative Name, or SAN, field. [Hostname verification]

  3. Does the server have the matching private key? The server must prove that it holds the private key paired with the certificate’s public key. [TLS authentication]

The app also checks the certificate’s expiry date and other validity requirements.

In this normal visit, the checks pass. The app sends the request for /photos. The API checks that Maya’s app is allowed to read the pictures, then returns them. HTTPS encrypts the request and response.

Maya sees the thumbnails. The server receiving the request is the service she intended to reach.

Then the attacker changes where the same name leads

On a later visit, the app starts a fresh connection and makes another DNS lookup. An attacker has arranged two things in our scenario.

First, they can manipulate the DNS answer used by this app. Instead of returning the photo service’s IP address, that lookup returns the address of the attacker’s machine. The API URL stored in the app is unchanged. It is still https://api.photos.example/photos.

Hostname the app wants: api.photos.example
Manipulated DNS answer: the attacker’s IP address
Connection reaches:    the attacker’s server

The attacker does not need to register api.photos.example or change the app to use attacker.example. Their machine can accept the connection and present a certificate claiming to represent the original name. The app must decide whether to trust that claim.

The attacker also needs a trusted-looking certificate

Second, they have a certificate the app will accept for that name. They have their own public/private key pair and a fraudulent certificate linking their public key to api.photos.example. Imagine it was issued after a trusted CA’s certificate-issuing systems were compromised.

Creating a key pair or a self-signed certificate alone would not satisfy the app’s normal trust checks. In our story, the fraudulent certificate leads to an accepted CA, covers the correct hostname, and passes the remaining certificate checks. The attacker has its matching private key.

The app generally accepts valid chains from its configured trusted CAs; it does not automatically require the same issuer or public key as the real server’s previous certificate. Here, an accepted issuer has vouched for the wrong party.

The request URL stays the same. The connection reaches someone else, whose certificate passes the app’s checks.

This story uses a manipulated DNS lookup to make the routing change concrete. An attacker controlling part of the network could also redirect traffic without changing the DNS answer. Neither route alone defeats correctly enforced certificate verification: the accepted fraudulent certificate is the other essential part of this example. [Names remain the reference for verification] [Certificate validation and interception]

The attacker forwards the conversation and reads it

The app accepts the attacker’s certificate and establishes an encrypted connection to that machine. It then sends the photo request it was going to send to the real API.

The attacker’s server opens a separate HTTPS connection to the real photo service, using the service’s correct address. It forwards the authorized request and relays the returned pictures back to the app. For this scenario, the API accepts the request and no other control stops the relay.

The attacker preserves the request’s meaning and the returned photos. They do not need to edit the album or insert a fake picture to benefit. Reading the conversation is enough.

They are not forwarding unreadable encrypted packets unchanged. Their server decrypts the app’s connection, reads the data, and sends it over the second encrypted connection. The response follows the same process in reverse.

SAME HOSTNAME · TWO HTTPS CONNECTIONS
Maya’s printing app
URL hostname: api.photos.example.
The manipulated lookup leads to the attacker.
HTTPS connection 1
Attacker’s server
Decrypts the request. Can read the returned photos.
Relays the same request and photos over a separate connection.
HTTPS connection 2
Real photo service
Checks the forwarded request and returns the photos.
Requests travel down this path; responses return up it. Both connections are encrypted, but the attacker is an endpoint of each. This is our fictional scenario, not a captured network trace.

The missing identity check

Maya sees the same thumbnails she expected. The attacker can see the pictures passing through their server. This is a man-in-the-middle attack: the attacker is the endpoint of two conversations while the app believes it has reached the intended service.

Why did the app let this happen? The attacker’s certificate named the right hostname and was signed by a CA the app still trusted. The app had checked those facts, but it had no rule requiring the real photo service’s particular public key.

The developers can add that missing rule. Before the app makes a request, they give it the public-key fingerprint it should expect from the photo service. The app then checks every new connection against that saved expectation. A fraudulent certificate with a different key can pass the ordinary checks and still fail this extra check. That is the protection SSL pinning adds.

How SSL pinning checks the expected public key

To set up that rule, the app’s developers get the expected public key from the photo-service team through their trusted deployment process. They do not ask the server reached through Maya’s network to choose which key the app should trust.

They calculate a compact fingerprint of the public-key information and save it in the app’s configuration. A hash function produces this fingerprint: the same input bytes produce the same result. The saved expectation is called a pin. This example assumes the two teams can coordinate the service’s keys and app updates.

Call the real server’s public key A and the attacker’s public key X. These letters are illustrative labels. Ordinary certificate validation did not previously require key A; SSL pinning adds that requirement.

SSL pinning requires this expected identity to match as well as the normal HTTPS checks. It narrows which server identities the app accepts for the configured API. [Apple identity pinning]

Now an accepted issuer and the right hostname are not enough. The app also asks, “Does this server certificate contain the public key we expected?”

How SSL pinning stops the same attack

The attacker manipulates the DNS answer again. The unchanged hostname still leads the app to their machine. Pinning does not repair that lookup; SSL pinning stops the app from trusting the wrong server once it arrives.

  1. The app starts the connection. Its request URL is still https://api.photos.example/photos. The manipulated lookup sends it to the attacker’s IP address. The photo request has not been sent.

  2. The attacker presents the fraudulent certificate. It covers the expected hostname and contains the attacker’s public key X.

  3. The ordinary checks pass in our scenario. The certificate chain is accepted, the hostname matches, and the attacker proves possession of the corresponding private key.

  4. The app checks its pin. It calculates the certificate’s public-key fingerprint. Key X’s fingerprint does not match the stored fingerprint for key A.

  5. The app refuses the connection. It sends no protected photo request over that connection and does not retry by removing the check. Maya sees a connection failure; the attacker receives no photos through this attempted relay.

When the app reaches the real service instead, its certificate passes ordinary validation and its public key A matches the pin. The app sends the authorized request and loads Maya’s photos normally. All required checks happen before the protected request is released; a pin match cannot excuse a failed ordinary check.

The three outcomes of SSL pinning

One request URL, three different outcomes
Where the connection reachesNormal checksExpected-key checkOutcome
Real service with key APassMatches AThe app can send the request.
Attacker with key X, without pinningPass in our scenarioNo configured pinThe attacker can read and relay the request.
Same attacker, with pinningPass in our scenarioX does not match AThe app stops before sending the request.

The attacker manipulates the route in both versions. With SSL pinning, reaching the attacker’s machine no longer leads to sending the photo request.

This trust failure happened: DigiNotar in 2011

Attackers compromised the certificate-issuance systems of DigiNotar, a CA trusted by major browsers. They obtained fraudulent certificates, including one for *.google.com. That name covered Google subdomains such as mail.google.com. Chromium’s account records hundreds of wrongly issued certificates. [Chromium’s DigiNotar account]

On 29 August 2011, Google reported attempted interception of users’ encrypted connections. Google said Chrome users were protected because Chrome detected the fraudulent certificate. Chrome already had built-in pins for covered Google services; the fraudulent chain lacked an expected public key. [Google’s incident report] [Chromium’s pinning explanation]

For people opening email, the risk included revealing passwords to an impersonating server. Mozilla warned that users on a compromised network could mistake a fraudulent site for the real one and disclose personal information. That is the same kind of misplaced trust illustrated by Maya’s fictional photo request. [Mozilla’s incident notice]

How the browser protection helped

DIGINOTAR · THE TRUST FAILURE
01
Certificate authority compromised
Attackers control certificate issuance.
02
Fraudulent Google certificate issued
Correct domain name. A key the attacker can use.
03
Attacker intercepts a connection
Presents the fraudulent certificate to the client.
Ordinary TLS validation

Issuer still trusted.
Hostname matches.
Other normal checks pass.

MAY ACCEPT THE CONNECTION
Chrome’s additional pin check

No expected public key
in the certificate chain.
The pinning rule fails.

REJECT THE CONNECTION
Simplified decision diagram for a client that still trusts the DigiNotar chain, before distrust updates. Chrome’s pins protected the covered Google services. This is an explanation of the checks, not a captured network trace.

Browser vendors also removed trust in DigiNotar, addressing exposure beyond sites protected by pins. Mozilla’s 2 September follow-up described more than 200 certificates for more than 20 domains, uncertainty about the breach’s full extent, and reports of certificates being used in actual attacks. [Mozilla’s follow-up]

The incident prompted requests to extend pinning. On 1 September, the Tor Project reported asking Google to add pinning and HTTPS enforcement for critical Tor sites. Pinning already existed; this compromise demonstrated its value. The historical Chrome protection was built into the browser, while our printing app uses its own configuration. [The Tor Project’s response]

Certificate pinning vs public key pinning

For SSL pinning in our story, the app pins the public-key information in the server’s own certificate, often called the leaf certificate. There are two common ways to express the expectation:

Two fingerprints with different renewal behaviour
What is fingerprintedWhat changes it
The whole certificateA newly issued certificate changes the fingerprint, even if it contains the same public key.
The certificate’s public-key informationA replacement certificate can keep the same fingerprint if that public-key information is unchanged. A new key needs a new accepted pin.

For developers, the public-key format commonly used is SubjectPublicKeyInfo, or SPKI. It contains the public key and its algorithm information. A SHA-256 SPKI pin is calculated like this:

Base64(SHA-256(DER-encoded SubjectPublicKeyInfo))

Here, DER is the standard binary encoding of that information. This is a hash of SPKI, not of the entire certificate or a private key. Use the representation required by your networking library. [OWASP pinning guidance]

The pin is public. Someone can copy it, but they still need the matching private key to complete the server’s authenticated handshake. A public-key pin does not require putting the server’s private key inside the app.

There is also a choice about whose key to pin. A server-key pin gives a narrow expectation. Pinning an intermediate or root CA accepts a broader set of otherwise valid certificates issued under that authority. A fraudulent certificate issued within that accepted set may still pass. [Choosing what to pin]

SSL pinning on Android and iOS

SSL pinning uses the same idea on both platforms: save the expected fingerprints for a hostname, then check the connection. The settings differ, including which certificate’s key can satisfy the check.

On Android 7.0 (API level 24) and later, developers can use Network Security Configuration. They put the hostname and its accepted SHA-256 SPKI fingerprints in an XML settings file, inside a pin-set. The app’s manifest points to that file. [Android platform support]

Android looks for a match anywhere in the validated certificate chain. A pin for the server’s key can match the server certificate; a pin for a CA’s key can match a CA certificate. In Maya’s example, the configured fingerprint belongs to the server’s own key. Adding a CA pin would allow more server keys than the one our story requires. [Android configuration and matching rules]

On iOS 14 and later, developers can configure pinning through App Transport Security, or ATS. In the app’s Info.plist, they put NSPinnedDomains inside NSAppTransportSecurity, then add the hostname and its pins. Each fingerprint uses the SPKI-SHA256-BASE64 field.

iOS separates two choices. NSPinnedLeafIdentities checks the server certificate’s key—the choice that follows Maya’s story. NSPinnedCAIdentities checks keys in CA certificates. If both lists are configured, iOS requires a match in each category. Within a list, multiple fingerprints provide accepted alternatives, such as a current key and a backup. [Apple’s domain settings] [Apple’s server-key fingerprints]

These settings protect connections made through networking APIs that honor them. Check the HTTP client or SDK your app actually uses. Keep the normal HTTPS checks, and plan for key changes before shipping the pins.

Is this the same as a website’s pinning header?

They share the idea of remembering expected public keys, but the rule reaches the client in different ways.

In our mobile app, the developers supply the pins in the app’s configuration. Its networking layer checks them before sending the protected request.

With the older browser feature called HTTP Public Key Pinning, or HPKP, the browser requested a page from the website’s web server. The server could include a Public-Key-Pins header in its HTTPS response. That header asked the browser to remember allowed public keys for later visits. The browser was the client receiving this instruction; the web server sent it. [HPKP server responses]

Chrome deprecated HPKP because it could also leave users unable to reach a site or let a hostile party impose harmful pins. [Chrome’s HPKP deprecation notice]

Sending that response header does not configure pinning in a native mobile app. And the pins that protected covered Google services during DigiNotar were already built into Chrome; they were not learned from this header.

SSL pinning and certificate renewal

Renewing a certificate does not always mean changing its public key. The photo service could get a new certificate that still contains key A. If its public-key information stays the same, the SPKI fingerprint stays the same too. The app’s existing public-key pin can still match, as long as the new certificate passes the normal HTTPS checks. A pin for the whole certificate would need updating because the certificate itself has changed. [Why public-key pins can survive certificate renewal]

Prepare the app before changing the key

Changing the key is different. We used one expected key, A, to explain the main check. A real SSL pinning deployment needs a plan for replacing keys, including accepted backup pins.

If the photo service changes to key B while installed apps accept only A, those apps reject the legitimate server too. Their rule is still working: the presented key no longer matches. The team must update the expectation before making that planned change.

  1. Prepare the replacement key. The server team keeps B’s private key secure and shares its public-key fingerprint through a trusted deployment process.

  2. Distribute A and B while the server still uses A. The app accepts either fingerprint. This collection of accepted alternatives is the pin set.

  3. Check installed-client coverage. Test supported app versions and determine which clients can accept B before switching the server.

  4. Switch to B with a valid certificate. Clients accepting B continue to pass both normal TLS validation and pinning.

  5. Prepare the next backup and retire A. Distribute B and the next planned key according to the migration policy.

A backup pin helps only if the team can deploy its matching key and a valid certificate. Apple and Android both document the need to plan for certificate or key changes. [Apple’s long-term strategy] [Android backup pins]

Emergency recovery needs its own plan. If A’s private key is stolen and its certificate is still accepted, apps that still accept A remain exposed to that identity. Changing the real server to B does not remove A from old apps.

Android can also give a pin set an expiration date. After that date, its pin check stops applying; it does not automatically learn a replacement pin. [Pin expiration]

What SSL pinning protects—and where it ends

SSL pinning restricts which server identities the app accepts for a configured connection. Its scope follows that connection.

  • An unsafe log can still expose data. Photos or credentials recorded after decryption need their own protection. Pinning does not redact them.

  • A compromised app can undermine its checks. An attacker who can change the app or its execution may bypass pinning or read data before encryption. [OWASP mobile testing guidance]

  • A gateway may be the server being checked. If a gateway or CDN ends the app’s TLS connection, the app checks that endpoint’s certificate. The gateway’s separate connection to an internal service needs protection too.

  • The API still checks permission. An accepted server connection does not decide which photos a request is allowed to read.

Should you add SSL pinning to your app?

The threat is real: a fraudulent certificate can let an attacker impersonate a service. SSL pinning can add protection, and Android and iOS provide ways to implement it.

But it comes with a risk. If the server changes its key before the app is ready, the app can block the real service and leave users unable to connect.

Use pinning when your app needs this extra protection and your team can manage the keys and updates. It is not a default requirement for every app; platform guidance urges caution. [Apple guidance] [Android guidance]

Back to Maya’s album

Maya still wants the same thing: to choose her family photos and print them.

Her app now has an expected key for the photo service. On the real connection, the certificate and pin checks pass, and the pictures load after authorization.

On the intercepted connection, the attacker’s different key fails the pin check. The app stops before sending the protected request. The attacker no longer gets Maya’s photos through the route we followed.

That is the change SSL pinning made in this story: the app moved from accepting any server certificate that passed its ordinary trust rules to requiring the server identity its developers had chosen in advance.

Explore more explanations in the security articles collection.

Filed under:
Previous article

0 responses

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.