Certificates and the Chain of Trust
Topic 46

Certificates and the Chain of Trust

Certificates

A certificate is a signed statement binding a public key to an identity — almost always a domain name. The server presents it during the handshake; the client checks that a Certificate Authority it already trusts vouched for that binding, that the name matches the site requested, and that the certificate is currently valid. Only then does the client believe the public key really belongs to the server it dialed.

Validation does not trust the leaf certificate directly. It walks a chain from the server's certificate up through one or more intermediates to a root the client's trust store already holds. Most "certificate errors" you will ever debug are chain, name, or expiry problems — and an expired certificate is the most common production outage that is not a code bug at all.

The chain of trust — leaf up to a preinstalled root
Root CA
self-signed, in the trust store
Intermediate CA
signed by the root; signs leaves
Leaf (server)
SAN: example.com, www.example.com

What a Certificate Contains

An X.509 certificate is a structured document with a handful of fields that matter operationally. The subject identifies who the certificate is for; the Subject Alternative Name (SAN) lists the actual hostnames it covers; the public key is the key being vouched for; the issuer names the CA that signed it; the validity period gives a not-before and not-after date; and the CA's signature over all of it makes the document tamper-evident.

The deprecated Common Name (CN) once held the hostname, but browsers stopped honoring it years ago — the SAN is now the only field that decides name match. A certificate whose SAN does not list the exact hostname (or a wildcard covering it) fails validation no matter what the CN says.

# dump a cert's fields, including the SAN list that decides name match
openssl x509 -in server.pem -noout -text
# Issuer: C=US, O=Let's Encrypt, CN=R3
# Validity: Not Before: Jun  1 ... Not After : Aug 30 ...
# Subject: CN=example.com
# X509v3 Subject Alternative Name:
#     DNS:example.com, DNS:www.example.com

The Chain of Trust

Trust flows from a small set of root CAs whose self-signed certificates ship preinstalled in every browser and operating system trust store. Roots are kept offline and almost never sign server certificates directly. Instead a root signs an intermediate CA certificate, and the intermediate signs your server's leaf certificate. The chain is leaf → intermediate(s) → root.

This indirection exists so a compromised intermediate can be revoked and replaced without burning the root that anchors millions of certificates. The client verifies each link's signature in turn: the root signed the intermediate, the intermediate signed the leaf. The moment the chain reaches a certificate already in the trust store, validation stops and succeeds — the trust store is the set of keys you have decided to believe a priori.

Validation Steps

A client accepts a certificate only if every check passes. The name must match: the requested hostname has to appear in the SAN list, directly or via a wildcard. The validity period must include the current time, which is why a wrong clock or an expired certificate breaks the handshake. The chain must be complete and signature-valid all the way to a trusted root. Any one failure aborts the connection.

Wildcards cover one label only: *.example.com matches api.example.com but not example.com itself nor a.b.example.com. That single-level limit is a frequent surprise — teams issue a wildcard expecting it to cover the apex domain and the bare name fails validation while every subdomain works.

Common Validation Failures

Four failures account for nearly every certificate incident. A missing intermediate happens when the server sends only its leaf; browsers often paper over it by fetching the intermediate via the certificate's AIA extension, but many API clients, mobile apps, and older libraries do not, so it "works in Chrome" and fails everywhere else. An expired certificate is the classic 3am page — validity lapsed and every client rejects it simultaneously.

A name mismatch means the SAN does not cover the hostname the client used, common after adding a new subdomain to an existing certificate's deployment. An untrusted root means the chain terminates at a CA the client's trust store does not hold — expected for an internal CA, fatal for a public site. Each error message points at exactly one of these, so read it before guessing.

Self-Signed vs CA-Signed vs Internal-CA

Self-signed certificates are signed by their own key, so no third party vouches for them and every client warns unless you manually trust each one. Fine for a throwaway test box or a local development server; never for anything a real client reaches in production.

CA-signed certificates from a public CA (Let's Encrypt, DigiCert) chain to a root already in every trust store, so any browser trusts them with no configuration — the right choice for public-facing sites. Internal-CA certificates chain to a private root you distribute to your own fleet; choose it for service-to-service traffic inside a network you control, where you do not want to expose internal hostnames to a public CA.

Common Mistakes
  • Serving the leaf certificate without its intermediates. Browsers with AIA fetching may hide the gap, but many API clients, mobile SDKs, and older runtimes reject the incomplete chain — so it passes your spot check and breaks in production for a subset of clients.
  • Letting a certificate expire. Validity is wall-clock checked, so every client rejects it at the same instant with no warm-up; the classic unmonitored-expiry outage takes down a whole service at once.
  • Issuing a certificate whose SAN does not cover the hostname. A new subdomain added without reissuing fails name match, and modern clients ignore the legacy CN entirely, so there is no fallback.
  • Expecting a wildcard to cover the apex or two label levels. *.example.com matches one label, so example.com and a.b.example.com both fail despite the wildcard looking comprehensive.
  • Trusting a self-signed certificate in production by pinning or disabling checks. It works until the cert changes or another team copies the bypass, and it removes the CA validation that would have caught an impersonator.
Best Practices
  • Serve the full chain — leaf plus every intermediate up to but not including the root — so clients without AIA fetching validate correctly on the first connection.
  • Test the chain from a clean client with openssl s_client -connect host:443 and confirm Verify return code: 0, rather than trusting a browser that may have cached the intermediate.
  • Put every hostname a service answers on in the SAN list, and remember a wildcard covers only one label, so add the apex name explicitly if clients hit it.
  • Monitor expiry and alert at least 30 days out, since an expired certificate fails all clients simultaneously and the renewal is the only fix.
  • Use a public CA for anything internet-facing and a private internal CA — distributed to your fleet's trust stores — only for traffic that stays inside a network you control.
Comparable conceptsLet's Encrypt (automated issuance, Topic 48)Code signing (same PKI idea)GPG web of trust

Knowledge Check

A site loads fine in Chrome but a mobile app gets a certificate error against the same endpoint. What is the most likely cause?

  • The server omits the intermediate, which Chrome fetches via AIA but the app does not
  • The certificate has expired, which only stricter mobile clients bother to enforce
  • The SAN does not list the hostname, so only the app rejects the name
  • The root CA is missing from the shared public trust store that both clients consult entirely

Why does a public CA sign your certificate with an intermediate rather than the offline root directly?

  • So a compromised intermediate can be revoked without invalidating the root
  • Because a longer chain makes the signature verification run faster on the client
  • Because the intermediate encrypts the traffic while the root handles authentication
  • Because clients ship the intermediate preinstalled and fetch the root per connection

A wildcard certificate for *.example.com is deployed. Which request fails its name check?

  • A request to the bare apex example.com, which a single-label wildcard misses
  • A request to api.example.com, exactly one label below the domain, from a service client
  • A request to www.example.com from a desktop browser
  • A request to staging.example.com over HTTP/2

You got correct