Skip to content

Introduction to HTTPS and PKI

Encryption: SSL/TLS worked out how two strangers derive a shared key over a connection an eavesdropper can watch in full — that's the encryption half of TLS solved. It deliberately left one gap open: ECDHE proves that whoever is on the other end of the connection can do correct Diffie-Hellman math, and nothing more. It says nothing about who they actually are. An attacker capable of intercepting the connection can run ECDHE exactly as correctly as the real server can, deriving a perfectly valid shared key with the client — the client just derived it with the wrong party. Encryption without identity verification protects a conversation from eavesdroppers while doing nothing to stop it from happening with an impostor in the first place. PKI (Public Key Infrastructure) is the system that closes that gap, and a certificate is its output.

What a certificate actually claims

HTTPS already introduced certificates at a practical level — a certificate binds a public key to a domain name, and a browser checks it against a trusted issuer before proceeding. What's worth unpacking here is the mechanism underneath that check: a certificate is a data structure, standardized by X.509 and profiled for internet use in RFC 5280, containing a subject (the domain it's issued for), a public key, a validity period, and — critically — a digital signature from whoever issued it.

That signature is what turns "here's a public key claiming to belong to example.com" into something a browser can actually trust, rather than an unverifiable assertion. Anyone can generate a key pair and claim it belongs to example.com; a self-signed certificate does exactly that, and browsers correctly reject it, since there's nothing behind the claim except the claim itself. A trusted certificate is trusted because something the browser already trusts vouched for it — which pushes the real question back one level: who does the browser already trust, and why?

The chain: root, intermediate, leaf

Browsers and operating systems ship with a built-in list of root certificate authorities (root CAs) — a relatively small set of organizations whose public keys are baked into the trust store ahead of time, with no certificate needed to vouch for them, because they're the actual foundation the rest of the system builds on. A root CA's own certificate is self-signed, and that's fine specifically because it isn't being trusted by virtue of a signature — it's trusted because the operating system or browser vendor decided, through a formal audit and inclusion process, to ship it as inherently trusted.

Root CAs almost never sign certificates for ordinary websites directly. Instead, a root CA signs one or more intermediate CA certificates, and those intermediates are the ones that actually sign the certificate a website presents — the leaf or end-entity certificate. RFC 5280 describes exactly this structure: "a chain of multiple certificates may be needed, comprising a certificate of the public key owner (the end entity) signed by one CA, and zero or more additional certificates of CAs signed by other CAs."

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>&1 | grep -A5 "Certificate chain"
Certificate chain
 0 s:CN = example.com
   i:C = US, O = SSL Corporation, CN = Cloudflare TLS Issuing ECC CA 3
   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
   v:NotBefore: May 31 21:39:12 2026 GMT; NotAfter: Aug 29 21:41:26 2026 GMT
 1 s:C = US, O = SSL Corporation, CN = Cloudflare TLS Issuing ECC CA 3

Entry 0 is the leaf certificate for example.com itself — s: is its subject, i: is its issuer. Entry 1 is the intermediate CA that issued it. The server sent both in this example; the root CA's own certificate typically isn't sent by the server at all, because the client is expected to already have it in its own trust store. The browser walks this chain backward: it checks that the leaf's signature verifies against the intermediate's public key, then checks that the intermediate's own signature verifies against a root it already trusts. If that walk reaches a trusted root with every signature checking out along the way, the chain is valid.

Root CA (self-signed, in the browser's trust store)
   │  signs
Intermediate CA certificate
   │  signs
Leaf certificate: example.com

A certificate is trustworthy exactly to the extent that its signature chain reaches a root the verifier already trusts — nothing about the domain name itself carries any authority on its own. This is why using an intermediate at all matters operationally, not just architecturally: if an intermediate's private key is ever compromised, that one intermediate can be revoked and replaced without touching the root CA's own key at all — the root, which every trust store depends on, stays offline and untouched through the entire incident.

What a certificate does and doesn't promise

Domain validation confirms control of a name, not the trustworthiness of what's hosted there

The overwhelming majority of certificates issued today — including every free certificate from Let's Encrypt — are domain-validated (DV): the CA confirms the requester genuinely controls the domain (commonly by having them publish a specific DNS record or serve a specific file), and issues the certificate on that basis alone. DV says nothing about who runs the site or whether its content is legitimate. A phishing site on a freshly registered look-alike domain can hold a perfectly valid, browser-trusted HTTPS certificate — the padlock confirms the connection is encrypted and that you're talking to whoever currently controls that specific domain name, not that the domain is safe or reputable. Extended Validation (EV) certificates once involved deeper organizational identity checks, but browser vendors have largely stopped displaying EV status distinctly in the address bar, judging that the distinction didn't meaningfully change user behavior.

Revocation: what happens when a certificate can't be trusted anymore

A certificate's notAfter date isn't the only way it can stop being valid. If a server's private key is stolen, or a certificate was issued in error, waiting out the remaining validity period isn't an option — the certificate needs to be invalidated immediately, before its stated expiry. That's revocation, and PKI has two mechanisms for checking it, standardized in RFC 5280 and RFC 6960 respectively.

  • CRL (Certificate Revocation List) is exactly what it sounds like: the CA periodically publishes a signed list of every certificate it has revoked, and a client checking a certificate's status downloads the current list and looks for it. The list grows over time and has to be fetched in full, which makes it a coarse, occasionally slow mechanism for something that's supposed to be checked on every connection.
  • OCSP (Online Certificate Status Protocol) replaces the bulk-list approach with a direct, per-certificate query: the client asks an OCSP responder "is this specific certificate, by serial number, still good?" and gets back a small signed response — good, revoked, or unknown — without downloading anything close to a full list.

OCSP has its own practical wrinkle worth knowing: querying the CA's OCSP responder on every single connection adds a round trip and, less obviously, tells the CA which sites a given client is visiting, in real time — a genuine privacy leak most browsers try to avoid. OCSP stapling fixes both problems by moving the query off the client entirely: the web server itself periodically queries the OCSP responder ahead of time, caches the signed response, and "staples" it directly onto the TLS handshake it sends to clients. The client gets the same freshness guarantee without ever contacting the CA itself, and the server has already paid the round-trip cost long before any client connects.

openssl s_client -connect example.com:443 -servername example.com -status </dev/null 2>&1 | grep -A15 "OCSP Response Status"
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
Version: 1 (0x0)
Responder Id: C = US, O = SSL Corporation, CN = Cloudflare TLS Issuing ECC CA 3
Produced At: Jul 24 19:14:00 2026 GMT
Responses:
Certificate ID:
  Hash Algorithm: sha1
  Issuer Name Hash: F0E907D20C31F211F5311C95A317FA0B9E01CB6C
  Issuer Key Hash: 8303FDE7F6F54A4D1541F4ED2216D3320A3ECA66
  Serial Number: 1AA73FEA257BE3334B9A29552E6F878E
Cert Status: good
This Update: Jul 24 19:14:00 2026 GMT
Next Update: Jul 31 19:14:00 2026 GMT

The -status flag requests OCSP stapling as part of the handshake; the Cert Status: good line near the bottom is the actual answer — this particular certificate isn't revoked — and This Update/Next Update show the responder's own refresh window for that answer. No output at all after that flag is a strong sign the server doesn't support stapling — not necessarily a problem, since the client can still fall back to querying OCSP or a CRL directly, just without the efficiency and privacy benefit of stapling.

Practical scenario: a certificate that's valid everywhere except one specific client

A newly deployed internal service, secured with a certificate from a public CA, works correctly in every browser and from every developer's laptop. One automated monitoring server, running a lightweight Linux container image, fails every HTTPS health check against it with a certificate verification error, even though the certificate is nowhere near expired and every other client accepts it without complaint.

curl -v https://internal-service.example.com/health
* SSL certificate problem: unable to get local issuer certificate
* Closing connection
curl: (60) SSL certificate problem: unable to get local issuer certificate

That's the actual output trimmed to the relevant lines — a real run also prints the full TCP and TLS handshake trace above this, which isn't the interesting part here. "Unable to get local issuer certificate" points directly at the chain-walking process this article just described: curl has the leaf certificate but can't find the intermediate CA certificate needed to walk the chain back to a trusted root. Full desktop operating systems and browsers typically ship with a broad, actively updated set of intermediate certificates cached locally, so this failure mode rarely shows up there. A minimal container image built for size, though, often ships a stripped-down CA bundle that's missing some intermediates — and if the web server itself isn't sending its intermediate certificate as part of the handshake (exactly the entry 1 shown in the chain example above), a client with an incomplete local bundle has no way to complete the walk at all.

The fix has two independent parts, and both matter: the server should always send its full chain, not just the leaf, specifically so clients don't have to already possess the right intermediate locally — this is a server misconfiguration if it isn't happening. And any minimal or custom container image expected to make HTTPS connections needs an up-to-date, complete CA certificate bundle (ca-certificates on Debian-based images) rather than assuming the base image already has one that's both current and complete.

Practice exercises

  1. Run the openssl s_client ... | grep -A5 "Certificate chain" command above against a real HTTPS site and identify how many certificates it returns. Is the root CA among them, and if not, why not?
  2. Explain, using the compromised-intermediate scenario from this article, why PKI's hierarchical structure is more resilient to a single compromised key than a system where every website's certificate was signed directly by one root CA.
  3. A colleague argues that since a phishing site can get a perfectly valid HTTPS certificate, HTTPS itself provides no real security benefit. Explain specifically what HTTPS does and does not protect against, using the DV-certificate note above.

Everything in this article has assumed the handshake itself — the actual sequence of messages that establishes a key and checks a certificate against this chain — already happened correctly. What that sequence actually looks like, message by message, is next.

Sources