Step 1 of 5 · Reading · ~2 min
Read
Production Concerns
X.509 Chain Validation
CertificateVerify proved the peer holds the leaf's private key. Now you have to decide whether the leaf is one you trust. That's path validation — walking from the leaf up through intermediates to a root in your trust store, and verifying signatures, names, and validity windows at every step.
What "valid" actually means
A simplified path validator (RFC 5280 §6 is the full version):
- The chain begins with the end-entity (leaf) certificate the peer sent.
- For each cert in the chain:
notBefore <= now <= notAfter— validity window must include the current time.- The cert's signature is valid under the next cert's public key.
issuerof this cert ==subjectof the next cert.
- The top of the chain is signed by some root in the trust store.
- (Production extras you would also check, but we don't here.)
basicConstraints CA=trueon every non-leaf.keyUsagepermitsdigitalSignatureon the leaf.extendedKeyUsageincludesserverAuth.SubjectAlternativeNamematches the requested hostname (RFC 6125).- Revocation: OCSP (RFC 6960) or CRLs (RFC 5280 §5).
- Certificate Transparency: SCT presence (RFC 6962).
- Name constraints, path length, policy mappings.
For this exercise we focus on the four core checks. Everything else is an extension you'd layer on.
Common failure modes
| Failure | Symptom |
|---|---|
| Wrong system clock | "expired" or "not_yet_valid" against perfectly fine certs |
| Server omitted the intermediate | "unknown_ca" — leaf's issuer doesn't match anything in your store |
| Wrong order in the chain | Many libraries handle out-of-order chains; some don't |
| Self-signed leaf | "unknown_ca" — the issuer matches the leaf itself, not a trusted root |
| Cross-signed root not in store | "unknown_ca" even though the same key signed a different trusted cert |
A real production validator does not stop at the first error and try the next root. cryptography's x509.verification (Python ≥ 42) and OpenSSL's X509_STORE_CTX_verify implement that.
Why we built it manually
In production: NEVER write your own path validator. Use the OS trust store and a vetted library — Python's cryptography.x509.verification.PolicyBuilder, Go's crypto/x509, OpenSSL's X509_verify_cert.
Hand-rolling it for this lesson gets you up close to the data structures. When the production validator fails, you'll know exactly which of these four checks tripped.
Ed25519 in certs (RFC 8410)
This lesson's test chains use Ed25519, not RSA, because the certs are 200-300 bytes each instead of ~1.3 KB. RFC 8410 defines the OID for Ed25519 in X.509; modern ACME (Let's Encrypt, ZeroSSL) does not yet issue Ed25519 leaves, but cryptography happily parses them.
The validation logic is identical regardless of algorithm — only verify_sig branches on the public-key type.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…