2.7
Cryptography for Engineers: Keys, Certificates and CAs
Symmetric vs asymmetric encryption, the certificate chain, how a CA vouches for a site, and automated short-lived certificates.
50 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Name the four goals of cryptography (confidentiality, integrity, authenticity, non-repudiation) and pick the right tool for each: encryption, hash, HMAC or signature.
- Explain symmetric encryption (AES-GCM, ChaCha20-Poly1305, AEAD, nonces) and why it needs a way to share keys.
- Explain public-key cryptography (RSA, elliptic curves): encrypting with a public key, signing with a private key, and why it is slow.
- Explain Diffie–Hellman key exchange with the paint-mixing picture, why it needs authentication, and what forward secrecy protects.
- Use hashes and HMACs correctly: download checks, webhook verification, HS256 vs RS256 tokens.
- Describe how HTTPS really combines these pieces (hybrid cryptography, corrected for TLS 1.3).
- Read an X.509 certificate, walk a chain of trust from leaf to root, and diagnose expired, missing-intermediate, unknown-root and wrong-name failures.
- Explain revocation (CRL, OCSP, stapling, and why Let’s Encrypt dropped OCSP), Certificate Transparency, ACME automation, the move to 47-day certificates, private CAs, key management and post-quantum key exchange.
#Where this lesson fits
Every time you see https:// in a browser, cryptography is doing three jobs at once: hiding the data, proving it was not changed, and proving who is on the other end. Lesson 1.9 introduced TLS from the outside: its three guarantees, the paint-mixing trick, the TLS 1.3 handshake and a first look at certificates. Lesson 1.3 signed cookies and JWTs with HMAC, and lesson 1.12 used mutual TLS for zero trust. This lesson opens the toolbox those lessons used: the building blocks (keys, ciphers, hashes, MACs, signatures) and the trust system on top of them (certificates, CAs, the chain, revocation, automation).
The next lesson, 2.8, puts the blocks together message by message: TLS 1.2 vs TLS 1.3 handshakes and mutual TLS. Storing passwords (slow, salted hashing) and managing keys at scale are in Chapter 34.
Two words first. Encryption turns readable data (plaintext) into unreadable data (ciphertext) using a key; decryption turns it back. When you type a password into a secure website, it leaves your laptop as ciphertext. Textbooks place encryption in the OSI presentation layer, but on the real internet it is done by TLS, which sits between the application and TCP (lesson 1.5 explains why that layer label is only a textbook view).
#What cryptography is for: four goals
| Goal | Question it answers | Threat it stops | Tool |
|---|---|---|---|
| Confidentiality | Can anyone else read it? | Eavesdropping on Wi-Fi, a tapped link, a stolen disk | Encryption (AES-GCM) |
| Integrity | Was it changed on the way? | Tampering, flipped bits, injected content | Hash (accidents only), HMAC, signature, AEAD tag |
| Authenticity | Who really sent it, or who am I talking to? | Impersonation, man-in-the-middle | HMAC (shared key) or signature + certificate |
| Non-repudiation | Can the sender later deny it? | “It wasn’t me” | Digital signatures only |
Availability, the other classic security goal, is not something cryptography provides; it can even hurt it (an expired certificate is an outage). Two rules frame everything else:
- Kerckhoffs’s principle: the algorithm is public, only the key is secret. AES, RSA and SHA-256 are published and studied by thousands of people; a system that relies on a secret algorithm is relying on luck.
- Do not invent your own cryptography. Use TLS for data in transit, and well-reviewed libraries (your platform’s TLS stack, libsodium, Go’s
crypto, Google’s Tink) for everything else. Most real breaks are in how primitives are combined, not in the primitives.
#Symmetric encryption: one shared key
In symmetric encryption the same secret key locks and unlocks:
Plaintext ──(key K)──▶ Ciphertext ──(same key K)──▶ Plaintext
- Fast and cheap. Modern CPUs have AES instructions (AES-NI on x86, and the same on most phone chips), so one core encrypts several gigabytes per second. That is why all bulk data (web pages, video, database files, backups) is encrypted symmetrically.
- Key sizes: 128 or 256 bits. Trying all 2¹²⁸ keys is beyond any computer that will ever exist; 256-bit keys are also comfortable against future quantum computers.
- The weakness: both sides need the same key, and you cannot send it over the network you are trying to protect. That is the key distribution problem, and it scales badly: n people who all talk privately need n(n−1)/2 keys, about half a million for 1,000 people.
#The algorithms you will meet
| Algorithm | Status | Where |
|---|---|---|
| AES-GCM (128 or 256-bit key) | The default | TLS, disk and database encryption, cloud KMS, almost everything |
| ChaCha20-Poly1305 | Modern, equally safe | TLS on phones and small devices without AES hardware, WireGuard, SSH |
| AES-CBC with a separate integrity check | Legacy, error-prone | Old TLS 1.2 suites, old file formats |
| DES (56-bit key) | Broken | Brute-forced in 56 hours in 1998 |
| 3DES | Deprecated | 64-bit blocks leak after a few gigabytes; NIST disallowed it for encryption after 2023 |
| RC4 | Broken | Banned from TLS in 2015 |
#AEAD: encryption that also detects tampering
Plain encryption only hides data. With older modes an attacker who cannot read a message can still flip bits in the ciphertext and flip the same bits in the plaintext, turning “500” into “900” without the key. AEAD (authenticated encryption with associated data) fixes this: AES-GCM and ChaCha20-Poly1305 output the ciphertext plus a 16-byte tag computed from the key and every ciphertext bit. Change one bit and decryption fails as a whole. “Associated data” is extra information (such as a record header) that is not secret but is covered by the tag. TLS 1.3 allows only AEAD ciphers.
AEAD needs a nonce (number used once), usually 96 bits, for every message. Reusing a nonce with the same key is catastrophic for GCM: an attacker can combine two ciphertexts to cancel the encryption and can forge tags. Use a counter or a random nonce, rotate keys long before 2³² messages, and prefer libraries that manage nonces for you (TLS derives them from record numbers).
Symmetric encryption: one key, and the problem of sharing it
Alice and Bob talk over a network that Eve can watch. With a shared key the traffic is unreadable; without one, getting the key across is the whole problem.
Scenario
Alice and Bob agreed a 256-bit key in person last week.
Alice encrypts “transfer 500 to Rahim” with key K and a fresh nonce (a number used once). The same key will unlock it, which is why it is called symmetric.
#Asymmetric (public-key) cryptography
Asymmetric cryptography uses a key pair made together: a public key that anyone may have and a private key that never leaves its owner. They are linked mathematically, but the private key cannot be computed from the public one. The pair is used in two opposite directions:
Encryption: Plaintext ──(Bob’s PUBLIC key)──▶ Ciphertext ──(Bob’s PRIVATE key)──▶ Plaintext
Signature: Message ──(Bob’s PRIVATE key)──▶ Signature ──(Bob’s PUBLIC key)──▶ valid / invalid
- Encryption: anyone can lock a message for Bob with his public key; only Bob can unlock it. No secret had to be shared beforehand.
- Signatures: only Bob can sign with his private key; anyone can verify with his public key. This is where authenticity and non-repudiation come from.
| Family | Hard problem | Typical sizes | Used for |
|---|---|---|---|
| RSA | Factoring the product of two large primes | 2048-bit keys at least, 3072 for long-term; 256-byte signatures | Certificate signatures, older key exchange |
| Elliptic curves (P-256, X25519, Ed25519) | Discrete logarithms on a curve | 256-bit keys ≈ RSA-3072 strength; 64-byte signatures | TLS key exchange (X25519), signatures (ECDSA, Ed25519), most new certificates |
#Why it is slow, and why that does not matter
The notes say public-key operations are about 1,000 times slower than symmetric ones. Order of magnitude, yes; the exact ratio depends on the operation. On one modern core, an RSA-2048 signature takes about a millisecond, an elliptic-curve operation a few tens of microseconds, while AES-GCM processes a megabyte in well under a millisecond. Two further limits matter more than speed: RSA-2048 can encrypt at most about 190 bytes at once, and a ciphertext is as long as the key. So public-key cryptography is never used for bulk data. It is used to agree on or deliver a small symmetric key, and to sign. That combination is hybrid cryptography, below.
Public and private keys: encrypting and signing
Bob has a key pair. The public key may go anywhere; the private key never leaves his server. The pair is used in two opposite directions.
Scenario
Alice wants to send Bob a short secret without any shared key.
Bob generates a key pair. The two keys are linked by mathematics (factoring for RSA, elliptic curves for X25519), but the private key cannot be computed from the public one.
#Key exchange: Diffie–Hellman and forward secrecy
Encrypting a fresh key with the receiver’s public key works, but there is a better way to get a shared key: let both sides compute it without ever sending it. That is Diffie–Hellman key exchange (1976), and its ephemeral elliptic-curve form, ECDHE, is how TLS 1.3 connections agree on their keys.
With toy numbers (public prime p = 23, base g = 5):
- The browser picks a secret a = 6 and sends A = 5⁶ mod 23 = 8.
- The server picks a secret b = 15 and sends B = 5¹⁵ mod 23 = 19.
- The browser computes 19⁶ mod 23 = 2; the server computes 8¹⁵ mod 23 = 2. Same secret.
- An eavesdropper saw 23, 5, 8 and 19. Getting a or b back from them is the discrete logarithm problem: easy for p = 23, hopeless for the 255-bit numbers of X25519.
Lesson 1.9 told this as paint mixing: a shared base colour, a secret colour each, swap the mixtures, add your own secret again, and both sides hold the same final colour while an onlooker cannot “unmix” it. The shared secret then goes through a key-derivation function that stretches it into the actual AES keys for each direction.
Diffie–Hellman is key agreement, not encryption: nothing is encrypted with it. And on its own it has a hole: it protects against people who listen, not against someone who can change messages. An active attacker can run one exchange with each side and sit in the middle. TLS closes the hole by having the server sign the handshake, including its key share, with its certificate’s private key. So the work is split cleanly: ECDHE provides the secret, signatures and certificates provide the identity.
#Forward secrecy
In the old TLS 1.2 “RSA key exchange”, the browser chose the session secret and encrypted it with the server’s long-term public key. Anyone who recorded the traffic and later obtained that private key (a leaked backup, a bug like Heartbleed in 2014, a court order) could decrypt every recorded session.
With ephemeral Diffie–Hellman (the E in ECDHE), each connection uses fresh random secrets that are erased after the handshake. The certificate key only signs. Stealing it later lets an attacker impersonate the server from then on (until the certificate is revoked or expires), but it does not unlock the past. This property is forward secrecy. TLS 1.3 (2018) removed RSA key exchange entirely, so every full TLS 1.3 handshake has it.
Diffie–Hellman: agreeing a secret in public
Toy numbers: public prime p = 23 and base g = 5. Real TLS uses X25519, with 32-byte values, and the same idea.
Scenario
Both sides end with the same secret; the eavesdropper sees every message and still cannot compute it.
The browser picks a random secret a = 6 and never sends it. In the paint analogy: a secret colour mixed into the shared yellow base.
#Hashing: fingerprints, not encryption
A cryptographic hash function such as SHA-256 turns any input into a fixed-size digest (32 bytes, 64 hex characters):
SHA-256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
A good hash is:
- Deterministic: same input, same output, everywhere.
- Fixed size and fast: a 4 GB file and a single word both give 32 bytes, at over a gigabyte per second.
- Avalanche: changing one bit of input changes about half the output bits.
- One-way (preimage resistant): given a hash, you cannot find an input for it.
- Collision resistant: nobody can find two different inputs with the same hash.
Uses everywhere in systems: checking downloads, content addressing (Git, container image digests like sha256:…), deduplication, cache keys and ETags, Merkle trees (Certificate Transparency, blockchains), and as the first step of every signature (you sign the hash, not the whole file).
MD5 and SHA-1 are broken for security: MD5 collisions take seconds, and in 2017 researchers published two different PDFs with the same SHA-1. The Flame malware (2012) used an MD5 collision to forge a code-signing certificate. Use SHA-256, SHA-384 or SHA-3.
Two limits to remember:
- A hash has no key, so it is not encryption and it proves nothing about who made the data. An attacker who changes a file can recompute its hash. A hash only protects integrity when the hash itself comes from a channel the attacker cannot touch.
- Passwords need special hashing. A fast hash lets an attacker test billions of guesses per second with GPUs. Passwords are stored with deliberately slow, salted functions (Argon2id, scrypt, bcrypt); that is Chapter 34.
Hashing: a fingerprint, not a lock
SHA-256 turns any input into 32 bytes. There is no key, and there is no way back.
Scenario
Deterministic: the same input always gives the same 256-bit value, on any machine. Written in hex it is 64 characters.
#Message authentication codes and HMAC
To prove integrity and origin with a shared secret, mix the key into the hash. The result is a message authentication code, a short tag that only key holders can compute. The standard construction is HMAC (hash-based message authentication code), for example HMAC-SHA256: it hashes the key and message in two nested rounds. Never build your own as SHA-256(secret + message): SHA-256 allows length extension, where an attacker who knows one such hash can compute a valid one for a longer message without knowing the secret. HMAC is immune.
#Webhooks: the everyday HMAC
When a payment provider or GitHub calls your webhook endpoint, how do you know the request is genuine? They sign it with a secret you both know:
- GitHub sends
X-Hub-Signature-256: sha256=<hex HMAC of the body>. - Stripe sends
Stripe-Signature: t=<timestamp>,v1=<hmac of "timestamp.body">.
Verifying correctly takes five rules:
- Compute the HMAC over the raw bytes received, before any JSON parsing; re-serialised JSON has different bytes.
- Compare with a constant-time function (
hmac.compare_digest,crypto.timingSafeEqual), so response timing does not reveal how many characters matched. - Include a timestamp in the signed data and reject anything older than a few minutes, which stops replay of captured requests.
- Store the event ID and process each event once: providers retry, and an HMAC proves origin, not freshness.
- Keep the secret in a secret manager, and support two secrets during rotation.
Verifying a webhook with HMAC
The payment provider and your service share one secret. Every webhook carries HMAC-SHA256(secret, timestamp + "." + raw body).
Scenario
The provider builds “1762070400.{"id":"evt_42","amount":500}” (timestamp, dot, the exact body bytes) and computes HMAC-SHA256 with the shared secret.
#HMAC vs signatures, and JWT algorithms
| HMAC (shared secret) | Digital signature (key pair) | |
|---|---|---|
| Keys | One secret, held by both sides | Private key at the signer, public key anywhere |
| Who can verify | Only secret holders | Anyone with the public key |
| Who can forge | Any verifier | Only the private-key holder |
| Non-repudiation | No: either side could have made it | Yes |
| Speed and size | Very fast, 32-byte tag | Slower, 64–256-byte signature |
| Typical use | Webhooks, signed cookies, internal tokens, API request signing | Certificates, software updates, tokens verified by many services |
This is exactly the JWT choice from lesson 1.3. With HS256, every service that verifies tokens holds the secret, so any one of them (or a leak from any one) can mint tokens for everybody. With RS256 or ES256, only the identity service holds the private key and publishes the public keys (often as a JWKS document); a compromised API service can verify tokens but not forge them. Always pin the expected algorithm when verifying: libraries that trusted the token’s own alg header once accepted none, or treated an RSA public key as an HMAC secret.
Self-checkQuestion
A partner says: “We sign every webhook with SHA-256 of the body, so it is secure.” Why is that not enough?
#Digital signatures
To sign, the sender hashes the message and runs the signing algorithm over the hash with the private key. To verify, anyone runs the verification algorithm with the public key, the message and the signature, and gets “valid” or “invalid”. A valid signature proves the message was signed by the holder of that private key and has not changed since.
| Algorithm | Signature size | Notes |
|---|---|---|
| RSA-2048 (RSA-PSS or the older padding) | 256 bytes | Still the most common for certificate chains |
| ECDSA P-256 | about 64–72 bytes | Most new TLS certificates; needs a perfect random nonce per signature |
| Ed25519 | 64 bytes | Fast, deterministic, hard to misuse: SSH keys, Git and package signing |
| ML-DSA-44 (post-quantum) | 2,420 bytes | Standardised in 2024; not yet in public web certificates |
Where signatures carry the internet: certificates (a CA signs a server’s key and name), the TLS handshake (CertificateVerify proves possession of the key), operating system and app updates, package managers, code signing, signed Git commits, DNSSEC, and JWTs with RS256 or ES256.
How signatures fail in real life:
- Bad randomness. ECDSA needs a fresh secret random number for every signature. In 2010 a games-console maker reused the same number, and hobbyists computed its master signing key from two signatures. Ed25519 and deterministic ECDSA remove this trap.
- Stolen keys. A signature proves which key signed, not which person. Non-repudiation is only as strong as the key’s protection, which is why signing keys live in HSMs.
- Weak hashes. A signature covers the hash; if the hash has collisions (MD5, SHA-1), the signature covers both colliding files.
#Hybrid encryption: how HTTPS really uses all of this
HTTPS needs both security and speed, so it combines asymmetric and symmetric cryptography. The notes describe two phases, and they are right in outline:
- Phase 1, identity and key exchange. At the start of the connection, public-key cryptography proves the server’s identity and establishes a fresh symmetric session key.
- Phase 2, bulk data. Once the session key exists, the actual HTML, JSON and images travel encrypted with fast symmetric AEAD (AES-GCM or ChaCha20-Poly1305).
The correction is in how phase 1 works. A popular explanation says the browser encrypts the session key with the server’s public key. That was TLS 1.2’s RSA key exchange, which had no forward secrecy and was removed in TLS 1.3. Today:
| Step | Primitive | What it achieves |
|---|---|---|
| Both sides send ECDHE key shares (X25519, now usually with ML-KEM) | Key agreement | A shared secret that never crossed the network, forward secret |
| Secret → traffic keys | Key-derivation function (built on HMAC-SHA256) | Separate keys for each direction and stage |
| Server sends its certificate chain | Signatures by CAs | Binds the server’s public key to its name |
| Server signs the handshake (CertificateVerify) | Signature with the certificate’s private key | Proves it owns that key, and that nobody altered the key shares |
| Finished messages | HMAC over the handshake transcript | Detects any tampering with the handshake |
| Records | AEAD (AES-GCM / ChaCha20-Poly1305) | Confidentiality and integrity of every byte of data |
The certificate’s key is never used to encrypt anything in TLS 1.3; it only signs. “Encrypt a random key with the recipient’s public key” is still the right description elsewhere: encrypted email and file tools (PGP, age), and the hybrid public-key encryption standard used by Encrypted Client Hello. Lesson 2.8 walks through both TLS handshakes message by message.
| Primitive | Gives | Keys | Typical example |
|---|---|---|---|
| Symmetric AEAD | Confidentiality + integrity | One shared key | AES-GCM records in TLS |
| Key exchange | A shared secret | Ephemeral key pairs | ECDHE / X25519 |
| Hash | Integrity against accidents, fingerprints | None | SHA-256 of a download |
| HMAC | Integrity + origin | One shared key | Webhook signatures, HS256 |
| Signature | Integrity + origin + non-repudiation | Key pair | Certificates, updates, RS256 |
#Certificates: binding a public key to a name
Public-key cryptography moved the problem from “share a secret” to “trust a public key”: a raw key says nothing about whose it is. A certificate fixes that. It is a digital passport: a document saying “this public key belongs to www.example.com, from date A to date B”, signed by a Certificate Authority (CA) that browsers already trust. Web certificates follow the X.509 standard (version 3). People still say “SSL certificate”; SSL itself has been dead for a decade, and the same certificates are used by TLS.
| Field | Example | Why it matters |
|---|---|---|
| Version, serial number | v3, 04:3f:… | The serial identifies the certificate for revocation |
| Signature algorithm | ecdsa-with-SHA384 | How the CA signed it |
| Issuer | O=Let’s Encrypt, CN=E6 | The CA certificate that signed this one: the next link in the chain |
| Validity | not before / not after | Outside this window the certificate is rejected |
| Subject | CN=example.com | Legacy field; browsers ignore the Common Name for matching |
| Subject public key | elliptic-curve P-256 key | The key this certificate vouches for |
| Subject Alternative Name | example.com, www.example.com | The list of names the certificate is valid for |
| Key usage, extended key usage | digital signature; server authentication | What the key may be used for |
| Basic constraints | CA:FALSE | Whether this certificate may sign other certificates |
| Authority information access, CRL distribution point | URLs | Where to fetch the issuer and the revocation list |
| Certificate policies | domain validated | Validation level |
| Embedded CT timestamps | 2–3 log signatures | Proof the certificate was publicly logged |
| Signature | 96 bytes | The CA’s signature over all of the above |
Practical details:
- Matching uses only the Subject Alternative Name list; Chrome stopped looking at the Common Name in 2017. A wildcard
*.example.comcovers exactly one label:www.example.comyes,example.comanda.b.example.comno. - Files: PEM is Base64 text between
-----BEGIN CERTIFICATE-----lines; DER is the same data in binary. A web server needsfullchain.pem(leaf + intermediates) and the private key file, which must be readable only by the server. - A certificate is public. It contains no secret; the private key is a separate file that never leaves the server (or its HSM).
- A certificate proves control of a name, not honesty: phishing sites get free certificates too. The padlock means “your connection is to this name, privately”, nothing more.
#The chain of trust
Browsers do not trust millions of website certificates directly. They trust a short list of root CAs in a trust store, and trust flows down a chain:
Root CA (self-signed, in the trust store, private key offline)
│ signs
▼
Intermediate CA (online, issues certificates every second)
│ signs
▼
Leaf certificate: www.example.com (sent by the server)
To validate, the client builds a path from the leaf to a root it trusts and checks every link:
- Each certificate’s signature verifies with the public key of the one above.
- Each one is inside its validity period today, including the intermediate.
- Every certificate above the leaf has
CA:TRUEand is allowed to sign. - The leaf’s name matches the host the client connected to, and its usage is server authentication.
- Nothing in the path is revoked, and (in Chrome and Safari) the leaf carries valid Certificate Transparency proofs.
- The top of the path is a root in the trust store.
Then the TLS handshake adds the last check: CertificateVerify proves the server holds the leaf’s private key.
Correction to the notes’ diagram. The notes draw DigiCert as the root and Let’s Encrypt as an intermediate “SSL provider” under it. They are two independent CAs. Let’s Encrypt, run by the non-profit ISRG, has its own roots (ISRG Root X1 for RSA, ISRG Root X2 for elliptic curves) and short-named intermediates such as R11 or E6. Until September 2021 another CA’s older root cross-signed it, so that old devices which did not yet know ISRG Root X1 would still trust it. DigiCert has its own separate roots.
#Trust stores
The roots come from more than one place:
- Operating systems: Windows, macOS and iOS, Android, each with its own root program.
- Browsers: Chrome has used its own Chrome Root Store since 2022, and Firefox uses Mozilla’s root store (which many Linux distributions also package as
ca-certificates). - Runtimes: Java has its own
cacerts; Python often uses thecertifibundle; Node.js bundles Mozilla’s list.
Root programs can remove a CA that breaks the rules, and do: a Dutch CA that was hacked in 2011 and issued fake Google certificates was removed and went bankrupt; Symantec’s CA business was distrusted in 2018; Chrome and Mozilla stopped trusting new Entrust certificates in late 2024. For system design this means: a slim container image without the CA bundle fails every outbound HTTPS call with “unable to get local issuer certificate”, and the root your clients trust can change under you.
#Why intermediates exist
- Protect the root key. A root is trusted by billions of devices for 15–25 years, and replacing it takes years of software updates. So its private key stays offline in an HSM in a vault, used only in recorded ceremonies to sign a few intermediates.
- Contain damage. If an intermediate is compromised or misbehaves, it can be revoked and replaced without touching the root in every device.
- Separate purposes. Different intermediates for RSA and elliptic curves, or for different products, with shorter lives that can be rotated.
The server must send its leaf plus all intermediates, never relying on the client to have them. Desktop browsers often hide a missing intermediate (they cache intermediates they have seen or fetch them from the URL in the certificate), so the site looks fine in Chrome while curl, Java, Go, Android apps and partner servers fail. “Works in the browser, fails from code” is the signature of a missing intermediate.
Walking the chain of trust
The server sends its leaf certificate and the intermediate. The browser must reach a root it already has in its trust store, checking every link on the way.
Scenario
What the browser received
| Certificate | Issued by | Valid until | Check |
|---|---|---|---|
| www.example.com | Example Issuing CA | 2027-04-20 | … |
| Example Issuing CA | Example Root | 2029-09-01 | … |
During the handshake the server sends two certificates: its own leaf for www.example.com and the intermediate that signed it. It does not send the root; the browser must already have that.
| What went wrong | Typical error | Usual cause |
|---|---|---|
| Leaf expired | NET::ERR_CERT_DATE_INVALID, certificate has expired | Renewal failed silently |
| Device clock wrong | Same date error on one machine only | Dead battery, NTP blocked; the certificate is fine |
| Intermediate missing | unable to get local issuer certificate (code), fine in browsers | Server configured with cert.pem instead of fullchain.pem |
| Intermediate or root expired | certificate has expired on older clients | Stale chain file; old devices without the new root |
| Unknown root | NET::ERR_CERT_AUTHORITY_INVALID, self-signed certificate in certificate chain | Private CA not installed, TLS interception proxy, or an attack |
| Wrong name | NET::ERR_CERT_COMMON_NAME_INVALID | Name missing from the Subject Alternative Name list, default certificate on a load balancer, missing SNI |
| Revoked | NET::ERR_CERT_REVOKED | Key compromise or mis-issuance |
Never “fix” any of these by disabling verification (verify=False, -k, InsecureSkipVerify). That accepts every attacker as well. Fix the chain, the name, the clock or the trust store.
#Who may issue: CAs, validation levels and the rules
A Certificate Authority is a trusted third party that checks a request and issues a signed certificate. Examples from the notes, updated: Let’s Encrypt (free, the largest by far), DigiCert, Sectigo (the CA formerly called Comodo CA, renamed in 2018), Google Trust Services. Cloudflare is not a classic public CA: the certificates on its edge come from partner CAs such as Let’s Encrypt and Google Trust Services, and its “Origin CA” certificates are trusted only by Cloudflare’s own proxies, not by browsers.
| Level | What the CA checks | Time and cost | What browsers show |
|---|---|---|---|
| DV (domain validated) | Control of the domain only (HTTP file, DNS record or TLS check) | Seconds, free with ACME | Normal padlock / site settings icon |
| OV (organisation validated) | Domain + that the organisation legally exists | Days, paid | Same as DV (details only inside the certificate) |
| EV (extended validation) | Domain + detailed legal and operational checks | Days to weeks, expensive | Same as DV since 2019, when Chrome and Firefox removed the special green bar |
Because users could not tell the difference and phishers bought OV and EV too, browsers stopped treating them specially; Chrome even replaced the padlock with a neutral “tune” icon in 2023. For most sites DV with automation is the right answer.
The rules are set by the CA/Browser Forum (CAs plus browser makers) in the Baseline Requirements, and enforced by the root programs. A few that matter to engineers:
- CAA records in DNS list which CAs may issue for your domain (
example.com. CAA 0 issue "letsencrypt.org"). CAs must check them since 2017. - Multi-perspective validation: since 2025 CAs must validate domain control from several network locations, so a local BGP or DNS hijack cannot trick them.
- No internal names: public CAs have not issued certificates for internal names (
db.internal,server01) or private IP addresses since 2015. - Revocation deadlines: 24 hours after a key compromise, 5 days for most other problems. In 2024 a CA had to revoke about 83,000 certificates within 24 hours over a validation bug; teams with automation simply reissued.
#Certificate Transparency
How would you know if a CA issued a certificate for your domain to someone else? Certificate Transparency (CT) makes every public certificate public. CAs submit each certificate to several independent, append-only CT logs (Merkle trees, so history cannot be rewritten) and embed the logs’ signed timestamps in the certificate. Chrome (since 2018) and Safari reject public certificates without them.
- Monitor your domains: services such as crt.sh or Cert Spotter, or your cloud provider, alert you when any certificate for your names appears. Mis-issuance has been caught this way many times.
- Side effect: every hostname you put in a public certificate is published forever.
staging-payments-v2.example.comin a certificate is visible to anyone. Use a wildcard or a private CA for names you do not want listed.
#Revocation: cancelling a certificate early
A certificate may need to die before its expiry date: its private key leaked, the CA issued it by mistake, or the domain changed hands. The CA revokes it. The hard part is telling every client.
| Method | How it works | Problem |
|---|---|---|
| CRL (certificate revocation list) | The CA publishes a signed list of revoked serial numbers; clients download it | Lists grew to many megabytes; clients rarely downloaded them |
| OCSP (online status protocol) | The client asks the CA’s responder about one serial during the handshake | Extra round trip; the CA learns which sites every user visits; if the responder is down or blocked, browsers soft-fail (accept anyway) |
| OCSP stapling | The server fetches a signed, short-lived OCSP answer and attaches it to the handshake | Fixes privacy and latency, but an attacker can simply omit the staple |
| Browser-pushed lists | The browser vendor collects every CA’s CRLs and pushes a compressed set (Firefox’s CRLite) or a curated subset (Chrome) | Works offline and privately; depends on the vendor’s update channel |
| Short-lived certificates | Certificates that expire in days | A stolen key soon becomes useless, with no revocation check at all |
Soft-fail is the core weakness of online checks: the attacker who stole the key can usually also block the OCSP request, so the check fails exactly when it matters. The industry has therefore moved away from OCSP. The CA/Browser Forum made OCSP optional and CRLs mandatory (2023), and Let’s Encrypt ended OCSP in 2025: it stopped accepting Must-Staple requests in January, removed OCSP URLs from new certificates in May and shut its responders down in August. Revocation now flows through CRLs, collected by browser makers and pushed to browsers, and is backed by ever-shorter lifetimes.
Revocation: cancelling a certificate early
The private key of shop.example.com was stolen, and its certificate is revoked. How does a browser find out?
Scenario
The browser receives a certificate that is in date and correctly signed. Nothing in it says it was revoked yesterday.
#Getting certificates automatically: ACME and Let’s Encrypt
Before 2015, certificates were bought, emailed and installed by hand once a year, and forgotten until they expired. Let’s Encrypt (public since December 2015) made DV certificates free and automatic, using a protocol it designed and the IETF standardised as ACME (Automatic Certificate Management Environment, RFC 8555, 2019).
How an ACME client gets a certificate:
- Create an account (identified by an account key pair) with the CA’s ACME server.
- Place an order for one or more names.
- For each name, complete a challenge that proves control.
- Send a CSR containing a public key generated locally; the private key never leaves your server.
- Download the certificate and chain, install it, reload the server. Repeat well before expiry.
| Challenge | Proof | Good for | Watch out |
|---|---|---|---|
| HTTP-01 | Serve a token at http://<name>/.well-known/acme-challenge/<token> on port 80 | Single web servers, simplest | Port 80 must be reachable; every server behind a load balancer must answer; no wildcards |
| DNS-01 | Create a TXT record _acme-challenge.<name> | Wildcards, internal servers, many servers | Needs DNS API credentials that can change your zone: scope and protect them |
| TLS-ALPN-01 | Answer a special TLS handshake on port 443 | TLS-terminating proxies without port 80 | Less widely supported |
Clients: certbot, Caddy (built in, as lesson 1.13 showed), cert-manager on Kubernetes, Traefik, acme.sh, and cloud managed certificates (AWS Certificate Manager, Google-managed certificates, Cloudflare) that renew for you. Let’s Encrypt has rate limits (for example 50 certificates per registered domain per week), so test against its staging environment. And since mid-2025 it no longer emails expiry warnings: monitoring is your job.
#Certificate lifetimes are shrinking
| From | Maximum lifetime of a public TLS certificate |
|---|---|
| 2015 and earlier | Up to 5 years, then 3 years, then 825 days (2018) |
| 1 September 2020 | 398 days |
| 15 March 2026 (now in force) | 200 days |
| 15 March 2027 | 100 days |
| 15 March 2029 | 47 days (and domain validation may be reused for only 10 days) |
The CA/Browser Forum voted for this schedule in April 2025. Shorter lifetimes limit the damage of a stolen key and of revocation that does not reach clients, and force automation. Let’s Encrypt already issues 90-day certificates by default, offers optional six-day ones, and has announced a move to 45 days by 2028.
What it means for your systems:
- Manual renewal is over. At 47 days, each certificate renews about eight times a year. Every certificate needs an automated owner.
- Monitor what is actually served, not what you think is installed: an external check of days remaining on every endpoint, alerting when a certificate passes about one third of its lifetime left (30 days on a 90-day certificate), plus alerts on renewal errors.
- Keep an inventory: load balancers, CDNs, partner-facing endpoints, internal services and IoT devices all hold certificates.
- Test the renewal path: firewall changes that close port 80, expired DNS API tokens and rate limits are the usual killers.
ACME: certificates without a human
An ACME client on your server (certbot, Caddy, cert-manager) talks to the CA’s ACME server. The CA only needs proof that you control the name.
Scenario
The client, identified by its own account key, orders a certificate for two names.
#Private CAs for internal services
Inside a company you often want certificates that public CAs will not or should not issue:
- Internal names (
orders.svc.cluster.local,db.internal), which public CAs may not certify. - Mutual TLS between services, where each service needs a client certificate; public CAs are dropping client authentication (Chrome’s root program requires server-only hierarchies, and Let’s Encrypt removed client authentication from its certificates in 2026).
- Very short lifetimes (hours) and high volume, without publishing every service name in CT logs.
The answer is a private CA: your own root, trusted only by your own machines. Common setups: a service mesh (Istio, Linkerd) that issues every workload a certificate valid for about 24 hours and rotates it automatically; cert-manager with a CA issuer on Kubernetes; HashiCorp Vault’s PKI engine; AWS Private CA; smallstep’s step-ca.
The trade-offs: you must distribute your root to every client (OS trust stores, container images, Java cacerts), keep the root key offline, run issuance as a critical service, and plan root rotation years ahead. The corporate TLS interception proxies from lesson 1.12 are a private CA too, installed on staff devices. Mutual TLS itself is lesson 2.8.
#Keys in production
The private key is the asset; certificates, signatures and encrypted data are only as safe as the key behind them.
- Generate keys where they will be used (on the server, or inside an HSM) and never move private keys by email, chat or tickets. A CSR carries only the public key.
- Never commit keys or secrets to Git. Use a secret manager, restrictive file permissions (
chmod 600), and secret scanning in CI. - HSMs and cloud KMS keep keys inside tamper-resistant hardware (certified to FIPS 140-3): your code asks the device to sign or decrypt, and the key never leaves. Cloud KMS services (AWS KMS, Google Cloud KMS, Azure Key Vault) add envelope encryption: data is encrypted with a data key, and that data key is encrypted by a master key in the KMS. Chapter 34 covers this in depth.
- Rotate keys on a schedule and immediately on suspicion. Publish new public keys before you start signing with them (a
kidin JWT headers lets verifiers pick the right key), and keep the old ones until everything signed with them has expired. - Use the operating system’s secure random generator (
/dev/urandom,secretsin Python,crypto.randomBytesin Node.js). In 2008 a Debian patch had left OpenSSL with only about 32,000 possible keys per key type for two years; every key generated on those systems had to be replaced. - Crypto agility: keep algorithms and key sizes in configuration and libraries up to date, so a broken algorithm (SHA-1 yesterday, RSA tomorrow) is a config change, not a rewrite.
#Post-quantum cryptography, briefly
A large enough quantum computer running Shor’s algorithm would break RSA and elliptic curves: key exchange and signatures. Symmetric ciphers and hashes are only weakened (Grover’s algorithm roughly halves their strength), so AES-256 and SHA-256 remain fine. Nobody can build such a machine today, but encrypted traffic recorded now could be decrypted later (“harvest now, decrypt later”), so key exchange had to move first.
- In August 2024 NIST published the first post-quantum standards: ML-KEM (FIPS 203) for key establishment and ML-DSA (FIPS 204) for signatures, plus a hash-based signature backup (FIPS 205).
- Hybrid key exchange
X25519MLKEM768combines classic X25519 with ML-KEM-768, so it is safe if either one holds. It is on by default in Chrome and Edge (since 2024), Firefox, Apple’s platforms (since 2025), Cloudflare, Go and OpenSSL 3.5. A large share of browser traffic is already protected this way. The cost is a bigger ClientHello (about 1.2 KB more), which broke some old middleboxes that assumed it fits one packet. - Signatures and certificates have not switched yet: an ML-DSA signature is about 2.4 KB against 64 bytes for elliptic curves, and a chain carries several signatures plus CT proofs. Browser makers and CAs are experimenting with new certificate designs to keep handshakes small. NIST’s draft timeline deprecates RSA and elliptic-curve algorithms from 2030 and disallows them by 2035.
For a system designer: keep TLS libraries, load balancers and CDNs current, avoid middleboxes that inspect handshakes, inventory where long-lived secrets and signatures are used (firmware signing, archives encrypted for decades), and value crypto agility.
#Cryptography in system design
- Terminate TLS at the edge, and re-encrypt inside when the network is shared or regulated. Use managed certificates (cloud certificate managers, Caddy, cert-manager) wherever possible.
- Treat certificate expiry as a reliability risk: automated renewal, external expiry monitoring, an inventory, and runbooks for mass reissuance.
- Serve the full chain, list every hostname in the Subject Alternative Name list, and include the CA bundle in container images.
- Keep clocks in sync (NTP): certificates, JWT expiry and webhook timestamps all depend on the time.
- Webhooks and internal calls: HMAC over raw bytes with a timestamp, constant-time compare, idempotent processing.
- Tokens verified by many services: prefer RS256 or ES256 with published public keys over a shared HS256 secret.
- Internal service identity: a private CA with short-lived certificates, usually through a service mesh.
- Encrypt data at rest with AEAD and KMS-managed keys, never with a key in the same database as the data.
- Watch CT logs for your domains, and set CAA records.
- Plan for change: 47-day certificates by 2029 and post-quantum algorithms are both scheduled; systems that need a human to touch them will break.
| Symptom | Likely cause | First check |
|---|---|---|
Site fine in browsers, curl or a Java client says “unable to get local issuer certificate” | Missing intermediate | openssl s_client -showcerts: how many certificates are sent? |
| Every outbound HTTPS call fails from a new container image | No CA bundle in the slim image | Install ca-certificates |
| Outage at exactly midnight UTC, all clients at once | Certificate expired | Expiry date, renewal logs, port 80 / DNS API access |
| Errors on one laptop only, “date invalid” | Wrong device clock | System time |
| All webhooks rejected after a framework upgrade | HMAC computed over parsed and re-serialised JSON | Verify over the raw body |
| Old Android phones or old API clients fail after a CA change | They do not have the new root | Chain choice, client trust store |
| An attacker mints admin tokens after one service leaked | Shared HS256 secret | Move to RS256 / ES256, rotate |
#Try it yourself
# Look at the certificate chain a server actually sends (count the "-----BEGIN" blocks)
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
# Decode the leaf: issuer, validity, Subject Alternative Name, key type, CT timestamps
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -text
# Just the expiry date (handy for a monitoring script)
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -enddate
# Hash: same input, same digest; change one letter and everything changes
echo -n hello | sha256sum
echo -n hellO | sha256sum
# HMAC-SHA256 of a webhook body with a shared secret
echo -n '1762070400.{"id":"evt_42","amount":500}' | openssl dgst -sha256 -hmac 'whsec_test_secret'
# Generate an Ed25519 key pair, sign a file and verify it
openssl genpkey -algorithm ed25519 -out priv.pem
openssl pkey -in priv.pem -pubout -out pub.pem
echo "release 1.2.3" > release.txt
openssl pkeyutl -sign -inkey priv.pem -rawin -in release.txt -out release.sig
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in release.txt -sigfile release.sig
# A strong random 256-bit key, as hex
openssl rand -hex 32
# Which CAs may issue for a domain?
dig +short CAA example.com
# Every certificate ever logged for a domain (Certificate Transparency)
curl -s 'https://crt.sh/?q=example.com&output=json' | head -c 600
# Practise ACME safely against Let's Encrypt staging
sudo certbot certonly --staging --webroot -w /var/www/html -d example.com --dry-run
In Chrome, click the icon left of the address bar → Connection is secure → Certificate is valid to see the chain, the Subject Alternative Name list and the validity dates. In DevTools → Security you can see the key exchange group (look for X25519MLKEM768) and the cipher (AES_128_GCM).
#Summary
Key takeaways
- Cryptography gives confidentiality (encryption), integrity (hash, MAC, AEAD tag), authenticity (MAC or signature + certificate) and non-repudiation (signatures only). Algorithms are public; keys are secret; never invent your own.
- Symmetric encryption (AES-GCM, ChaCha20-Poly1305) is fast and protects all bulk data. Use AEAD so tampering is detected, never reuse a nonce, and remember the key distribution problem. DES, 3DES and RC4 are legacy.
- Asymmetric cryptography (RSA, elliptic curves) uses a key pair: public encrypts / private decrypts, private signs / public verifies. It is slow and RSA can encrypt only a few hundred bytes, so it handles keys and signatures, not data.
- Diffie–Hellman / ECDHE lets two sides compute a shared secret that never crosses the network; signatures stop active man-in-the-middle; ephemeral keys give forward secrecy, mandatory in TLS 1.3.
- Hashes (SHA-256) are keyless fingerprints, not encryption; MD5 and SHA-1 are broken. HMAC adds a shared key: webhooks, signed cookies, HS256. Signatures let anyone verify and only one party sign: certificates, updates, RS256/ES256.
- HTTPS is hybrid: ECDHE (now with ML-KEM) for the key, signatures and certificates for identity, AEAD for the data. The server’s certificate key is not used to encrypt a session key in TLS 1.3.
- A certificate (X.509) binds a public key to names in the Subject Alternative Name list for a validity period, signed by a CA. Clients walk leaf → intermediate → root in their trust store; roots stay offline, intermediates do the work, and servers must send the full chain.
- Let’s Encrypt has its own roots (ISRG), not DigiCert’s. DV is the norm; EV no longer shows anything special. CAA, multi-perspective validation and Certificate Transparency limit and expose mis-issuance.
- Revocation via OCSP soft-fails and leaks privacy; Let’s Encrypt ended OCSP in 2025, browsers push CRL data, and short-lived certificates are the real fix.
- ACME automates issuance (HTTP-01, DNS-01, TLS-ALPN-01). Lifetimes: 200 days since March 2026, 100 in 2027, 47 in 2029. Automate, monitor what is served, keep an inventory.
- Private CAs serve internal names and mTLS; keys belong in HSMs/KMS and get rotated; post-quantum hybrid key exchange is already the default, signatures come next.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Which goal can a digital signature provide that an HMAC cannot?
Self-checkQuestion
Why is all bulk data on the internet encrypted with symmetric ciphers such as AES-GCM?
Self-checkQuestion
A team writes its own file encryption with AES in a mode without any authentication tag. An attacker cannot read the files. What can still go wrong?
Self-checkQuestion
A service encrypts records with AES-GCM using one key and a hard-coded nonce. Which two consequences are real?
Choose 2
Self-checkQuestion
Which statement about DES and 3DES is correct today?
Self-checkQuestion
1,000 employees must each be able to talk privately with every other employee using only symmetric keys. How many keys are needed?
Self-checkQuestion
Bob publishes his public key. Which statement is correct?
Self-checkQuestion
An attacker on café Wi-Fi intercepts a user’s request for the server’s public key and returns his own key instead. What prevents this in HTTPS?
Self-checkQuestion
In the toy Diffie–Hellman exchange with p = 23 and g = 5, Alice’s secret is 6 and Bob’s is 15. Alice sends 8 and Bob sends 19. What is the shared secret, and what does an eavesdropper know?
Self-checkQuestion
A company recorded years of a competitor’s TLS traffic and has just obtained the competitor’s server private key. Which sessions can it decrypt?
Self-checkQuestion
Why must the server sign its key share (CertificateVerify) in TLS, even though Diffie–Hellman already keeps the secret from eavesdroppers?
Self-checkQuestion
Which two statements about SHA-256 are correct?
Choose 2
Self-checkQuestion
An attacker breaks into a project’s website and replaces both the download and the SHA-256 checksum printed next to it. What would have protected users?
Self-checkQuestion
Why is HMAC used instead of simply SHA-256(secret + message)?
Self-checkQuestion
A webhook endpoint verifies HMAC signatures correctly, but an attacker resends a captured, unmodified refund webhook two hours later. Which two measures stop this?
Choose 2
Self-checkQuestion
Twelve microservices verify JWTs signed with HS256 using one shared secret. One service is compromised. What is the risk, and the better design?
Self-checkQuestion
Which statement about digital signatures is correct?
Self-checkQuestion
A popular explanation says: “In HTTPS, the browser encrypts a session key with the server’s public key.” For TLS 1.3, what is accurate?
Self-checkQuestion
A certificate has Common Name www.example.com and the Subject Alternative Name list example.com, api.example.com. Which hostnames will Chrome accept it for?
Self-checkQuestion
A site works in Chrome, but a Java service and curl fail with “unable to get local issuer certificate”. What is the most likely cause?
Self-checkQuestion
Why do CAs issue website certificates from intermediate CAs instead of directly from their roots?
Self-checkQuestion
The notes’ diagram shows DigiCert as the root and Let’s Encrypt as an intermediate under it. What is correct?
Self-checkQuestion
A new slim container image makes every outbound HTTPS call fail with certificate errors, while the old image works. What is the likely fix?
Self-checkQuestion
Which statement about DV, OV and EV certificates is correct today?
Self-checkQuestion
What does Certificate Transparency give a domain owner?
Self-checkQuestion
Why did online OCSP checking fail as a defence against a stolen key?
Self-checkQuestion
Which two describe revocation today, after Let’s Encrypt ended OCSP?
Choose 2
Self-checkQuestion
You need a certificate for *.example.com from Let’s Encrypt. Which ACME challenge must you use?
Self-checkQuestion
An auto-renewing 90-day certificate expired and took the site down at midnight. Renewal had been failing for a month because a firewall change closed port 80. Which two measures would have prevented the outage?
Choose 2
Self-checkQuestion
According to the CA/Browser Forum schedule, what is the maximum lifetime of a new public TLS certificate in November 2026, and where is it heading?
Self-checkQuestion
A platform team needs certificates for 2,000 internal services named like orders.svc.cluster.local, used for mutual TLS and rotated daily. What is the right approach?
Self-checkQuestion
Which statement about post-quantum cryptography is correct as of late 2026?