TLSIntermediate

How a certificate is verified

“When your browser connects to a site over HTTPS, how does it decide that the server’s certificate is trustworthy? What can make that check fail even though the certificate is genuine?”

What this tests

  • The chain of trust: leaf, intermediates, trust-store root
  • The individual checks beyond signatures: name, validity, key usage, revocation
  • Client-specific behaviour — browsers vs curl vs Java vs old devices
  • The production failure catalogue and how to read each error string

Answers by level

Read the beginner answer first and notice what is missing.

A certificate is a signed statement: *this public key belongs to these names, valid between these dates, according to this issuer*. The client does not trust the statement because it is well-formed; it trusts it because it can build a path from it to a root it already holds. The server sends its leaf and the intermediate(s); the root is *not* sent — it must already be in the client’s trust store (the OS store, or the browser’s own, e.g. Mozilla’s for Firefox and the Chrome Root Program). For each link the client verifies the signature with the issuer’s public key, checks the issuer is allowed to issue (basicConstraints: CA, path length, key usage), and stops when it reaches a trusted root. See Certificates and the Chain of Trust.

Then the leaf itself: the requested hostname must match a Subject Alternative Name (browsers ignore the Common Name); the current time must fall between notBefore and notAfter; the extended key usage must include server authentication; the signature algorithm and key size must be acceptable (SHA-1 and RSA-1024 are rejected); and the certificate must not be revoked — checked via OCSP, an OCSP response stapled by the server, or the browser’s own summary (CRLSets in Chrome, CRLite in Firefox). Chrome and Safari also require proof the certificate was logged in Certificate Transparency logs. Finally the handshake proves the server holds the *private* key: CertificateVerify in TLS 1.3, or the key exchange itself in 1.2 (The TLS Handshake). A stolen certificate without the key is useless.

A genuine certificate fails in ways that are all about the *client’s* view. Missing intermediate: the server sends only the leaf; browsers often recover by fetching the issuer from the AIA URL or from cache, so the site "works in Chrome" while curl, Java, Python and Go fail with unable to get local issuer certificate. Unknown root: a corporate or private CA not installed on this machine, or a *new* public root missing from an old trust store — when Let’s Encrypt’s cross-sign from DST Root CA X3 expired in 2021, old Android devices and old OpenSSL 1.0 clients rejected perfectly valid chains. Clock skew: a device with the wrong date sees "not yet valid" or "expired". Hostname mismatch: the right certificate on the wrong vhost, a missing SNI so the default certificate is presented, or api.example.com reached via an IP address. Expired: the most common outage of all — Let’s Encrypt certificates last 90 days, and automation that silently stops renewing shows up exactly 90 days later.

Green flags · Red flags

Strong green flag · Says validity is a property of certificate *plus verifier* and gives the Let’s Encrypt root-expiry or a cross-signing example.
Green flags
  • Explains the chain and that the root comes from the client’s trust store, not the server
  • Lists the leaf checks: SAN match, validity window, key usage, revocation, CT
  • Knows the server must prove possession of the private key
  • Names the missing-intermediate case and why browsers hide it
  • Can map at least two error strings to the check that failed
Red flags
  • Believes the CA is contacted during every handshake to confirm the certificate
  • Thinks the certificate itself encrypts the session
  • Cannot explain why a site works in a browser but fails in curl
  • Suggests disabling verification (-k, NODE_TLS_REJECT_UNAUTHORIZED=0) as a fix

Follow-up questions

F1
Your API works in Chrome but every Python client fails with CERTIFICATE_VERIFY_FAILED. First hypothesis?
F2
Why does a wrong system clock cause TLS errors?
F3
What does OCSP stapling change?

Scenario

At 09:00 on a Tuesday, every mobile app user on Android 7 starts getting TLS errors against your API while web users are fine; nothing was deployed. Explain what most likely changed, how you confirm it in two commands, and the trade-off in the fix.

Learn this topic