Security Notes
Networking

Encryption, TLS, and Cryptography Deep Dive

74 min read 29 sections 29 model answers verified 2026-10

The Big Picture: Four Primitives

PrimitivePurposeExamplesKey Property
Symmetric encryptionConfidentialityAES-GCM, ChaCha20-Poly1305Fast; same key for enc/dec
Asymmetric encryptionKey exchange, digital signaturesRSA, ECDH, ECDSASlow; public/private key pair
HashingIntegrity, fingerprintingSHA-256, SHA-3, BLAKE3One-way; fixed output length
MAC/HMACAuthenticity + integrityHMAC-SHA256, Poly1305Requires shared secret

Rule of thumbAsymmetric is used to establish a shared secret, then symmetric takes over for bulk data. This is TLS in a nutshell.


AES (Advanced Encryption Standard)

WhatBlock cipher operating on 128-bit blocks. Key sizes: 128, 192, or 256 bits.

Why AESSelected by NIST in 2001 after open competition. Hardware acceleration (AES-NI instruction) makes it extremely fast on modern CPUs. Considered quantum-resistant for 256-bit keys (would need 2^128 operations with Grover's algorithm).

WhereEverywhere — TLS, disk encryption (FileVault, BitLocker, LUKS), VPNs, WiFi (WPA2/WPA3), SSH, cloud KMS.

AES Modes of Operation

ModeTypeAuth?IV reuse safe?Use Case
ECBBlockNoN/ANEVER (identical blocks → identical ciphertext → pattern leak)
CBCBlockNoNo (IV must be random)Legacy TLS, disk encryption (with separate HMAC)
CTRStreamNoNo (nonce must be unique)Fast streaming encryption
GCMStream + AEADYes (128-bit tag)NoRecommended — TLS 1.3, SSH, IPSec
CCMBlock + AEADYesNoIoT, IEEE 802.11 (WiFi)
SIVStream + AEADYesYes (nonce misuse resistant)Key wrapping, deterministic enc

AES-GCM deep dive

  • Combines CTR mode (encryption) with GHASH (authentication tag)
  • Produces: ciphertext + 128-bit authentication tag
  • Decryption MUST verify tag before decrypting (otherwise oracle attacks)
  • Critical weakness: nonce reuse → catastrophic (reveals authentication key AND plaintext XOR)
  • Used in: TLS 1.2 ECDHE-RSA-AES256-GCM-SHA384, TLS 1.3 AES_256_GCM_SHA384
python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = os.urandom(32)        # 256-bit key
nonce = os.urandom(12)      # 96-bit nonce (never reuse with same key!)
data = b"secret message"
aad = b"authenticated but not encrypted"

aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, data, aad)   # ciphertext + 16-byte tag
plaintext = aesgcm.decrypt(nonce, ciphertext, aad)

Advantages

  • AEAD: encryption and authentication in one pass
  • Hardware-accelerated (PCLMULQDQ + AES-NI)
  • Widely supported and well-studied

Disadvantages

  • Nonce misuse is fatal (unlike AES-SIV)
  • Nonce is 96 bits — if randomly generated, risk of collision after ~2^48 messages with same key (birthday bound)
  • Side-channel vulnerable in software without constant-time implementation

ChaCha20-Poly1305

WhatStream cipher (ChaCha20) + MAC (Poly1305). AEAD.

Why ChaCha20Designed by D.J. Bernstein as an alternative to AES for systems without hardware AES acceleration (e.g., older ARM chips). Resistant to timing side-channels in software because it uses only XOR, addition, rotation.

WhereTLS 1.3 (TLS_CHACHA20_POLY1305_SHA256), WireGuard VPN, Signal Protocol, OpenSSH (chacha20-poly1305@openssh.com).

AES-GCMChaCha20-Poly1305
Speed (with HW)FasterSlower
Speed (no HW)SlowerFaster
Side-channelRisky in SWSafe (no table lookups)
Nonce reuseCatastrophicStill bad but less catastrophic
Nonce size96 bits96 bits
Key size128 or 256 bits256 bits

Advantages

  • Software performance on mobile/IoT
  • Inherently constant-time

Disadvantages

  • No hardware acceleration (slower on AES-NI chips)
  • Less deployment history than AES

RSA

WhatAsymmetric encryption based on integer factoring hardness. Key sizes: 2048, 3072, 4096 bits.

Why RSAFirst practical public-key system (1977). Used for key exchange (RSA key transport in TLS 1.2) and digital signatures.

WhereTLS certificates (signing), S/MIME, PGP, SSH host keys (legacy), JWT (RS256).

How it works (simplified):

Key generation:
  p, q = large primes
  n = p × q              (public modulus)
  e = 65537              (public exponent, standard)
  d = e^-1 mod φ(n)      (private exponent)

Encryption:   c = m^e mod n
Decryption:   m = c^d mod n
Signature:    sig = hash(m)^d mod n
Verification: hash(m) = sig^e mod n

RSA vs ECC key strength comparison

RSA bitsECC bitsSecurity level
102416080-bit (broken)
2048224112-bit (minimum)
3072256128-bit (recommended)
4096384192-bit

Advantages

  • Well-understood, widely deployed
  • Simple conceptually
  • Large ecosystem of implementations

Disadvantages

  • Large key and signature sizes (vs ECDSA)
  • Slow operations vs ECC
  • Quantum-vulnerable (Shor's algorithm breaks RSA)
  • Vulnerable to timing attacks without blinding
  • PKCS#1 v1.5 padding attacks (Bleichenbacher); use OAEP/PSS

Padding schemes

SchemeUseNotes
PKCS#1 v1.5Legacy enc/sigVulnerable (Bleichenbacher for enc, ROBOT)
OAEPEncryptionRecommended; probabilistic; provably secure
PSSSigningRecommended; probabilistic

Elliptic Curve Cryptography (ECC)

WhatAsymmetric cryptography using elliptic curves over finite fields. Smaller keys for equivalent security.

Why ECCSmaller keys = faster operations, less bandwidth, better for constrained devices. Also no known sub-exponential attacks (unlike RSA factoring).

WhereTLS (ECDHE, ECDSA), Bitcoin/Ethereum (secp256k1), SSH (ed25519), Signal (Curve25519), TLS certificates (ECDSA), JWT (ES256).

Common Curves

CurveSizeUseNotes
P-256 (secp256r1)256-bitTLS, ECDSA, ECDHNIST curve; some concern about backdoor
P-384 (secp384r1)384-bitHigh-security TLSGovernment/compliance use
secp256k1256-bitBitcoin, EthereumNon-NIST; performance-optimised
Curve25519 (X25519)255-bitECDH key exchangeDesigned by Bernstein; fast, safe
Ed25519255-bitSignatures (EdDSA)Fast, constant-time, safe to implement

ECDH (key exchange)

Alice: ephemeral private key a, public key A = a·G
Bob:   ephemeral private key b, public key B = b·G
Shared secret: S = a·B = b·A = ab·G

ECDSA (signing)

  • Requires a random k per signature
  • Critical: reusing k with same private key reveals the private key (PlayStation 3 attack, Bitcoin key extractions)
  • EdDSA (ed25519) uses deterministic k → immune to this

Advantages

  • Smaller keys and signatures than RSA
  • Faster operations
  • Ed25519: deterministic, constant-time, immune to k-reuse

Disadvantages

  • More complex to implement correctly (side-channel risks)
  • Quantum-vulnerable (Shor's algorithm breaks ECDLP too)
  • Curve choice matters (NIST curves have some controversy)

Diffie-Hellman Key Exchange (DH / ECDH)

WhatTwo parties derive a shared secret over an untrusted channel without transmitting the secret.

WhyFoundation of forward secrecy. Neither party needs to encrypt the key — it's derived from ephemeral values.

Classic DH (finite field):
  Public: prime p, generator g
  Alice: secret a → A = g^a mod p → sends A to Bob
  Bob:   secret b → B = g^b mod p → sends B to Alice
  Alice: S = B^a mod p = g^ab mod p
  Bob:   S = A^b mod p = g^ab mod p
  Both have S, attacker only saw A, B, g, p

ECDH uses the same concept over elliptic curves (much smaller key for same security).

Forward SecrecyUse ephemeral DH (DHE or ECDHE) — generate new keypair per session. If long-term private key is compromised later, past sessions cannot be decrypted because ephemeral keys are discarded.

ProtocolKey ExchangeForward Secrecy?
TLS 1.2 with RSARSA key transportNo
TLS 1.2 with ECDHEEphemeral ECDHYes
TLS 1.3ECDHE (mandatory)Yes (always)

Hashing Algorithms

AlgorithmOutputSpeedStatus
MD5128 bitsFastBroken (collision attacks) — only for non-cryptographic checksums
SHA-1160 bitsFastBroken (SHAttered collision 2017) — deprecated in TLS, certificates
SHA-256256 bitsMediumSecure — widely used
SHA-384384 bitsMediumSecure — higher security margin
SHA-512512 bitsFast on 64-bitSecure
SHA-3/Keccak224-512 bitsSlowerSecure — different construction (sponge)
BLAKE2256/512 bitsVery fastSecure — faster than SHA-2 in software
BLAKE3256 bitsFastestSecure — parallelisable, very fast
bcrypt~60 charsIntentionally slowPassword hashing — adaptive cost
Argon2VariableIntentionally slowRecommended for passwords — memory-hard
scryptVariableIntentionally slowPassword hashing — memory-hard

Where each is used

  • SHA-256: TLS signatures, certificates, Bitcoin PoW, Git commit IDs, HMAC-SHA256
  • SHA-512: HMAC in SSH (hmac-sha2-512), high-security signatures
  • SHA-3: Available in TLS 1.3, government applications requiring post-SHA-2 algorithm
  • BLAKE3: Fast checksums, general-purpose (Cargo, Zig, Cloudflare)
  • Argon2id: Password hashing (OWASP recommendation), key derivation
  • bcrypt: Legacy password hashing (web apps) — work factor 10-12 typical

TLS in Depth

Last verified2026-10

What is this?TLS (Transport Layer Security, the successor to SSL) is the protocol that puts the "S" in HTTPS, SMTPS, IMAPS, LDAPS and most service-to-service traffic. It sits between TCP (or QUIC) and the application, and turns an untrusted network path into a private, tamper-evident channel to a server whose identity has been checked. The sections below go from first principles to byte-level detail: the cryptography it uses, certificates and PKI, cipher suites, the record layer, both handshakes, the defenses built around TLS, the attacks that shaped it, and the TLS 1.3 key schedule.

Why it mattersAlmost every security question about the web eventually lands on TLS: "how do you know you're talking to the real bank?", "what does a MITM proxy actually do?", "why did we disable TLS 1.0?", "why can't the SOC see inside this traffic anymore?". Interviewers use TLS to probe whether you understand cryptography as a system: which primitive does which job, and what fails when one is missing.

The primitives themselves (AES, ChaCha20, RSA, ECC, Diffie–Hellman, hashing, HMAC, HKDF) are covered earlier on this page. This part is about how TLS assembles them.

1 · Fundamentals

What TLS guarantees (and doesn't), who the players are, how versions evolved.

2 · Crypto in TLS

Which primitive does which job in the handshake and the record layer.

3 · Certificates

X.509 structure, extensions, private keys, CSRs, file formats.

4 · Validation & revocation

Chains, constraints, DV/OV/EV, CRL, OCSP, CRLSets, CRLite, short-lived certs.

5 · Cipher suites

Key exchange, authentication, bulk encryption, hashing; forward secrecy; what to allow.

6 · Records & handshakes

The record layer, TLS 1.2 handshake variants, resumption, mutual TLS, extensions.

7 · Defenses & attacks

HSTS, CAA, Certificate Transparency; BEAST to Raccoon and what each one taught.

8 · TLS 1.3

What changed, why, the key schedule, PSK, 0-RTT, and the 1.3 extensions.


TLS Fundamentals: Goals, Players and Versions

What TLS protects

Confidentiality

Only the two endpoints can read the data. Provided by symmetric encryption (AES-GCM, ChaCha20-Poly1305) with keys agreed during the handshake.

Integrity

Any change to the data in transit is detected. Provided by a MAC (TLS 1.2 CBC suites) or an AEAD tag (GCM / Poly1305).

Authentication

The client knows it's talking to the real server (and optionally vice versa). Provided by certificates + digital signatures.

Anti-replay

A recorded record can't be re-injected later. Every record has an implicit sequence number mixed into the MAC/nonce, so a replayed or reordered record fails verification.

Warning

TLS does not give non-repudiationBoth sides hold the same symmetric session keys, so either side could have produced any given record. A packet capture of a TLS session can't prove to a third party that the server sent a particular message. That needs an application-level signature (e.g. signed JWTs, S/MIME, document signing). Interviewers like this trap.

The players

Client

Starts the connection, offers its capabilities, validates the server's certificate. Usually a browser, an SDK or a service.

Server

Picks the parameters, proves its identity with a certificate and a signature made with its private key.

Certificate Authority (CA)

A third party both sides trust. It verifies the server controls the domain, then signs a certificate binding the domain name to the server's public key.

Root store

The list of CA roots a client trusts out of the box (Chrome Root Store, Mozilla NSS, Apple, Microsoft). Root program policy is what actually governs CAs.

CT logs

Public, append-only logs every publicly trusted certificate must be submitted to, so mis-issuance can be spotted.

Versions

VersionYearSpecWire valueStatus
SSL 2.01995Netscape0x0002Prohibited (RFC 6176, 2011) — broken by design
SSL 3.01996RFC 6101 (historic)0x0300Deprecated (RFC 7568, 2015) after POODLE
TLS 1.01999RFC 22460x0301Deprecated (RFC 8996, 2021) — BEAST, weak PRF
TLS 1.12006RFC 43460x0302Deprecated (RFC 8996, 2021) — no AEAD, SHA-1/MD5 PRF
TLS 1.22008RFC 52460x0303Acceptable with AEAD + ECDHE suites only
TLS 1.32018RFC 84460x0304Preferred everywhere
Memory hook

"TLS 1.0 is SSL 3.1"The wire version for TLS 1.0 is 3,1 (0x0301), TLS 1.2 is 3,3, TLS 1.3 is 3,4. The rename from SSL to TLS was mostly political (Netscape → IETF), not a redesign. That's why "SSL certificate" and "SSL offload" are still everyday terms, even though no one should be running SSL.


How TLS Uses Cryptography

TLS is a hybrid system. Asymmetric cryptography is slow but solves the "how do two strangers agree on a secret and know who they're talking to" problem. Symmetric cryptography is fast and protects the bulk data. Hashes glue everything together.

Which primitive does which job in a TLS connection
  1. Key exchange — Diffie–Hellman (ECDHE)Handshake

    Both sides send a public value, and each combines the other's with its own private value to reach the same shared secret. The secret itself never crosses the wire. (TLS 1.2 also allowed RSA key transport: the client encrypted a secret to the server's RSA key.)

  2. Authentication — digital signaturesHandshake

    The server signs handshake data with the private key matching its certificate (RSA-PSS, ECDSA or Ed25519). The CA's signature on the certificate binds that key to the domain name.

  3. Key derivation — hash-based KDFHandshake

    The raw shared secret is stretched into separate keys and IVs for each direction: the TLS 1.2 PRF (HMAC-SHA256/384) or TLS 1.3 HKDF.

  4. Transcript integrity — hashingHandshake

    A running hash of every handshake message is signed (CertificateVerify) and MACed (Finished), so tampering with any earlier message is caught.

  5. Bulk protection — symmetric AEADRecords

    Application data is encrypted and authenticated with AES-GCM or ChaCha20-Poly1305, using the derived keys and a per-record nonce.

PrimitiveTLS 1.2 uses it forTLS 1.3 uses it for
RSAKey transport or signaturesSignatures only (RSA-PSS for handshake signatures)
(EC)DHEOptional key exchangeThe only full-handshake key exchange (plus PSK)
ECDSA / EdDSASignatures (ECDSA)Signatures (ECDSA, Ed25519, Ed448)
DSARare, allowedRemoved
HashPRF, Finished, signaturesHKDF, transcript hash, signatures
SymmetricAES-CBC+HMAC, AES-GCM, ChaCha20, (RC4, 3DES)AEAD only: AES-GCM, ChaCha20-Poly1305, AES-CCM
Memory hook

"RSA wears two hats; TLS 1.3 took one away." RSA can encrypt a secret (key transport) or sign (authentication). Key transport means whoever later steals the server's private key can decrypt every recorded session, so TLS 1.3 kept only the signing hat. That single change is why TLS 1.3 always has forward secrecy.


X.509 Certificates, Keys and CSRs

What's in a certificate

A certificate is a signed statement from a CA: "the holder of this public key is entitled to use these names, from this date to that date". The structure is ASN.1, encoded in DER (binary) and usually shipped as PEM (base64 between -----BEGIN CERTIFICATE----- lines).

X.509 v3 certificate — the three top-level parts
tbsCertificatemost bytes
signatureAlgorithmsmall
signatureValue64–512 B
tbsCertificate
"To Be Signed": version, serial, issuer, validity, subject, public key, extensions
signatureAlgorithm
Which algorithm the CA used, e.g. ecdsa-with-SHA384, sha256WithRSAEncryption
signatureValue
The CA's signature over the DER bytes of tbsCertificate
text
Certificate:
  Version: 3
  Serial Number: 04:a1:...              ← unique per CA; ≥ 64 bits of CSPRNG output (CA/B Forum rule)
  Signature Algorithm: ecdsa-with-SHA384
  Issuer: C=US, O=Let's Encrypt, CN=E6  ← must byte-match the issuer cert's Subject
  Validity:
    Not Before: Jun  1 00:00:00 2026 GMT
    Not After : Aug 30 00:00:00 2026 GMT
  Subject: CN=example.com               ← informational only; browsers ignore CN for matching
  Subject Public Key Info:
    Public Key Algorithm: id-ecPublicKey (P-256)
    Public Key: 04:6b:... (65 bytes)
  X509v3 extensions:
    Subject Alternative Name: DNS:example.com, DNS:www.example.com
    Basic Constraints: critical, CA:FALSE
    Key Usage: critical, Digital Signature
    Extended Key Usage: TLS Web Server Authentication
    Authority Key Identifier: 93:27:...
    Subject Key Identifier: 1f:c2:...
    Authority Information Access: CA Issuers - URI:http://e6.i.lencr.org/
    Certificate Policies: 2.23.140.1.2.1    ← CA/B Forum "Domain Validated" policy OID
    CT Precertificate SCTs: (2 SCTs from different log operators)

Certificate extensions that matter

ExtensionWhat it saysSecurity relevance
Subject Alternative NameDNS names / IPs the cert is valid forThe only field used for hostname matching
Basic ConstraintsCA:TRUE/FALSE, optional pathLenConstraintA leaf with CA:TRUE could mint certificates; old IE bugs ignored this
Key UsageLow-level operations: digitalSignature, keyEncipherment, keyCertSign, cRLSignStops a leaf key being used to sign certificates
Extended Key UsagePurpose: serverAuth, clientAuth, codeSigning, emailProtectionA code-signing cert must not authenticate a web server
Name ConstraintsPermitted / excluded name subtrees for a CALets an org get a CA restricted to *.corp.example
AKI / SKIKey identifiers of issuer / subjectHelps path building pick the right issuer when names collide
Authority Information AccessURL of the issuer cert (and formerly OCSP)Lets clients fetch missing intermediates ("AIA chasing")
CRL Distribution PointsWhere to download the CRLRevocation
Certificate PoliciesOIDs for DV / OV / EV and CA policyHow a client knows the validation level
SCT listSigned Certificate Timestamps from CT logsRequired by Chrome and Apple for public trust
TLS Feature (must-staple)Server must staple an OCSP responseHard-fail revocation (RFC 7633); rarely deployed

Critical flagAn extension marked critical must be understood by the client, or the certificate must be rejected. Basic Constraints and Key Usage are normally critical; SAN is critical only when the Subject is empty.

What's in a private key

RSA private key

Modulus n, public exponent e (65537), private exponent d, primes p and q, plus CRT helpers dP, dQ, qInv that speed up signing ~4×. Knowing p or q is as good as knowing d.

EC private key

A single random integer (the scalar) and the curve name, e.g. P-256. The public key is that scalar × the curve's base point.

Ed25519 key

A 32-byte seed, hashed to produce the signing scalar. No curve parameters to choose, no per-signature randomness to get wrong.

Storage formats

PKCS#1 (BEGIN RSA PRIVATE KEY, RSA only), PKCS#8 (BEGIN PRIVATE KEY, any algorithm), encrypted PKCS#8 (BEGIN ENCRYPTED PRIVATE KEY), SEC1 (BEGIN EC PRIVATE KEY).

Matching a certificate to its private key. They match if they contain the same public key. Compare hashes of the public key, not of the files:

bash
openssl x509 -in cert.pem -noout -pubkey | openssl sha256
openssl pkey -in key.pem -pubout          | openssl sha256
openssl req  -in req.csr  -noout -pubkey  | openssl sha256   # the CSR should match too

What's in a CSR

A Certificate Signing Request (PKCS#10) is what you send a CA: your subject, your public key, the extensions you'd like (usually SANs), all signed with your private key. That self-signature is proof of possession: you can't request a certificate for someone else's public key. The private key never leaves your machine.

bash
# New P-256 key + CSR with SANs in one step
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout example.key -out example.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
openssl req -in example.csr -noout -text -verify      # inspect and check the self-signature

File formats

FormatEncodingTypical extensionsHolds
PEMBase64 + header lines.pem .crt .cer .keyOne or more certs, or a key
DERRaw binary ASN.1.der .cerExactly one cert or key
PKCS#7PEM or DER.p7b .p7cCertificates/chain only, no private key (common on Windows/Java)
PKCS#12Binary, password-protected.pfx .p12Private key + cert + chain in one bundle
JKSJava proprietary.jksJava keystore (PKCS#12 is now the Java default)
bash
openssl x509 -in cert.pem -outform der -out cert.der                    # PEM → DER
openssl x509 -in cert.der -inform der -out cert.pem                     # DER → PEM
openssl pkcs12 -export -inkey key.pem -in cert.pem -certfile chain.pem -out bundle.pfx
openssl pkcs12 -in bundle.pfx -nodes -out everything.pem                # PFX → PEM (key unencrypted!)
openssl pkcs7 -in chain.p7b -print_certs -out chain.pem                 # P7B → PEM

Inspecting real certificates

bash
# Show the chain a server actually sends, with SNI
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

# Decode the leaf
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName,basicConstraints,extendedKeyUsage

Building a private CA (the lab version)

bash
# 1. Root CA: self-signed, CA:TRUE, long-lived, keep offline
openssl req -x509 -new -newkey ec -pkeyopt ec_paramgen_curve:P-384 -nodes -days 3650 \
  -keyout root.key -out root.pem -subj "/CN=Lab Root CA" \
  -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"

# 2. Issuing CA: signed by the root, pathlen:0 = may only sign leaves
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout int.key -out int.csr -subj "/CN=Lab Issuing CA"
openssl x509 -req -in int.csr -CA root.pem -CAkey root.key -days 1825 -out int.pem \
  -extfile <(printf "basicConstraints=critical,CA:TRUE,pathlen:0\nkeyUsage=critical,keyCertSign,cRLSign")

# 3. Leaf: CA:FALSE, serverAuth, SANs
openssl x509 -req -in example.csr -CA int.pem -CAkey int.key -days 90 -out example.pem \
  -extfile <(printf "basicConstraints=critical,CA:FALSE\nkeyUsage=critical,digitalSignature\nextendedKeyUsage=serverAuth\nsubjectAltName=DNS:example.com,DNS:www.example.com")

openssl verify -CAfile root.pem -untrusted int.pem example.pem           # → example.pem: OK

Certificate Validation and Chains

Why there is a chain

Root CA keys are too valuable to use online, so roots sign a handful of intermediate (issuing) CAs, and those sign the end-entity ("leaf") certificates. If an intermediate is compromised, it can be revoked without replacing the root in every device on earth.

flowchart TB
  R["Root CA — self-signed<br/>lives in the client root store<br/>key kept offline in an HSM"]
  I1["Intermediate CA E6<br/>CA:TRUE, pathlen 0"]
  I2["Intermediate CA R11<br/>CA:TRUE, pathlen 0"]
  L1["Leaf: example.com<br/>CA:FALSE, EKU serverAuth"]
  L2["Leaf: api.example.net<br/>CA:FALSE, EKU serverAuth"]
  R --> I1 --> L1
  R --> I2 --> L2

The server sends leaf + intermediates (never needs to send the root: the client must already have it). The client builds a path from the leaf to any root it trusts and validates every link.

What the client checks on each certificate in the path
  1. Issuer name and key identifier match the next certificate upLink

    Subject of the issuer = Issuer of the child; AKI of the child = SKI of the issuer.

  2. Signature verifies with the issuer's public keyLink

    With an allowed algorithm: no MD5 or SHA-1 for certificate signatures; RSA ≥ 2048 bits.

  3. Inside its validity periodLink

    Every certificate in the path, including the intermediates.

  4. Issuer is allowed to be an issuerLink

    basicConstraints CA:TRUE, keyUsage keyCertSign, and the path length so far doesn't exceed any pathLenConstraint above it.

  5. Names are inside any name constraintsLink

    If an intermediate is constrained to .corp.example, a leaf for bank.com below it is invalid.

  6. Leaf-only checksLeaf

    Hostname in SAN, EKU serverAuth, not revoked, enough SCTs, lifetime within root-program limits.

Path building is harder than it looks

Cross-signing

A newer root can be signed by an older, widely trusted root so old devices still trust it. That creates multiple valid paths, and the client must pick one that works.

The Let's Encrypt 2021 lesson

When the cross-signing DST Root CA X3 expired (30 Sep 2021), clients with broken path building (old OpenSSL 1.0.x) picked the expired path and failed, even though a valid path via ISRG Root X1 existed.

Missing intermediate

Browsers often hide this misconfiguration by caching intermediates or fetching them via AIA. curl, Java, Go and mobile apps don't, so the same site "works in Chrome, fails in the API client".

Wrong order / extra root

Servers should send leaf first, then each issuer. Sending the root is harmless but wastes bytes. Sending an unrelated intermediate can confuse strict clients.

DV, OV and EV

TypeWhat the CA verifiesHow it showsNotes
DV (Domain Validated)You control the domain: HTTP file, DNS TXT record, or TLS-ALPN challenge (ACME)Padlock onlyThe vast majority of certificates; free and automated
OV (Organisation Validated)DV + the organisation legally existsPadlock only; org name visible in cert detailsPolicy OID 2.23.140.1.2.2
EV (Extended Validation)OV + stricter identity vettingWas a green bar with the company nameChrome 77 / Firefox 70 (2019) removed the EV UI: research showed users didn't notice its absence, and names could be registered to collide
Memory hook

"DV proves the domain, not the company." Every certificate proves control of a name. Only the name is cryptographically checked by the browser. paypa1.com can get a perfectly valid DV certificate. That's why phishing sites have padlocks, and why "look for the padlock" is no longer security advice.


Revocation

Last verified2026-10

What is this?Revocation is how a CA says "stop trusting this certificate before its expiry date", for example because the private key leaked or the certificate was mis-issued. It is the weakest part of web PKI: every mechanism has failed on privacy, reliability or scale, which is why the industry is moving to short-lived certificates instead.

MethodHow it worksProsCons
CRL (Certificate Revocation List)CA publishes a signed list of revoked serial numbers; client downloads itSimple, cacheableLists grow large; refresh lag of hours to days
OCSP (Online Certificate Status Protocol, RFC 6960)Client asks the CA's responder about one serialSmall, near real-timePrivacy leak (CA sees every site you visit); adds latency; usually soft-fail
OCSP stapling (RFC 6066 status_request)Server fetches its own signed OCSP response and sends it in the handshakeNo privacy leak, no extra client round tripServer must keep the staple fresh; clients still soft-fail if it's missing
OCSP Must-Staple (RFC 7633)Cert extension: "reject me if there's no staple"Turns soft-fail into hard-failOutages if the server's stapling breaks; rarely deployed
CRLSets (Chrome)Google crawls CRLs and pushes a curated subset with browser updatesLocal, private, fastOnly covers high-impact revocations, not every certificate
CRLite (Firefox)Compresses all revocations of publicly trusted certs into a filter cascade, pushed dailyComplete, local, privateBrowser-specific infrastructure
Short-lived certificatesCertificates expire before revocation would matterNo revocation infrastructure neededRequires fully automated issuance (ACME)
sequenceDiagram
  participant C as Client
  participant S as Server
  participant O as CA OCSP responder
  Note over S,O: Background, every few hours
  S->>O: OCSP request for my serial
  O-->>S: Signed response "good", valid for days
  Note over C,S: During the handshake
  C->>S: ClientHello + status_request
  S->>C: Certificate + stapled OCSP response
  Note over C: Verify CA signature<br/>on the staple. No call<br/>to the CA, no leak
Important

The soft-fail problemIf an OCSP check times out, almost every client continues rather than breaking the site. But an attacker who has a revoked certificate and sits on the network path can simply block the OCSP request. So plain OCSP stops nothing in exactly the scenario revocation exists for. (Adam Langley's classic line: soft-fail revocation is "like a seat belt that snaps when you crash".)

Where things stand (2026)The CA/Browser Forum made OCSP optional for CAs and requires CRLs. Let's Encrypt removed OCSP URLs from its certificates and shut its OCSP responders down in 2025. Browsers check revocation locally (CRLSets, CRLite). Maximum certificate lifetimes are being cut under ballot SC-081:

Maximum public TLS certificate lifetime (CA/Browser Forum ballot SC-081)
  1. Until 15 March 2026Before398 days

    Roughly 13 months, in force since 2020 (Apple led by policy, the Forum followed).

  2. From 15 March 2026Now200 days

    Domain-validation reuse also shrinks.

  3. From 15 March 2027Next100 days
  4. From 15 March 2029Final47 days

    Domain-validation reuse drops to 10 days. Manual certificate renewal effectively stops being possible, so automation (ACME) becomes mandatory.


Cipher Suites

What is this?A cipher suite is a named bundle of algorithms the two sides agree to use. In TLS 1.2 the name spells out all four jobs: key exchange, authentication, bulk encryption, and the hash used for the PRF/MAC. TLS 1.3 slimmed it down to just the bulk cipher and hash, because key exchange and authentication are negotiated separately.

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  │       │    │    │      │
  │       │    │    │      └── Hash for the PRF (and HMAC in non-AEAD suites)
  │       │    │    └────────── Symmetric cipher + mode
  │       │    └─────────────── Certificate / signature algorithm (authentication)
  │       └──────────────────── Key exchange
  └──────────────────────────── Protocol

TLS_AES_128_GCM_SHA256          ← TLS 1.3: only AEAD cipher + HKDF hash

The four components

Key exchange

RSA (client encrypts a secret to the server's key: no forward secrecy), DH/ECDH static (no FS), DHE/ECDHE ephemeral (fresh key per handshake: forward secrecy). TLS 1.3: (EC)DHE or PSK.

Authentication

How the server proves its identity: RSA or ECDSA signature (TLS 1.3 adds Ed25519/Ed448), or a pre-shared key. anon suites have none and are trivially MITM'd.

Bulk encryption

AES-GCM and ChaCha20-Poly1305 (AEAD, preferred), AES-CCM (IoT); legacy AES-CBC + HMAC; broken RC4, 3DES, DES, export ciphers, NULL.

Hash / PRF

SHA-256 or SHA-384 drives the PRF (1.2) or HKDF (1.3), and the HMAC in CBC suites. MD5 and SHA-1 suites are obsolete.

Forward secrecy, visually

Forward secrecy (often called "perfect forward secrecy", PFS) means that stealing the server's long-term private key later does not let you decrypt traffic you recorded earlier.

Why RSA key exchange fails the "record now, steal key later" attack
  1. 2026: an adversary records your encrypted TLS sessionsAttacker

    They can't read them yet, but storage is cheap.

  2. 2028: the server's RSA private key leaksAttacker

    Heartbleed-style bug, stolen backup, insider, or a legal order.

  3. With RSA key exchange: every recorded session is now readableRSA kex

    The pre-master secret was encrypted with that RSA key in each ClientKeyExchange. Decrypt it, re-derive the session keys, and read everything.

  4. With ECDHE: nothing is recoverableECDHE

    Each session's secret came from ephemeral DH keys that were thrown away after the handshake. The long-term key only signed the exchange. Stealing it lets you impersonate the server in future, but not decrypt the past.

Avoid, accept, prefer

VerdictTLS 1.2 suitesWhy
PreferECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-GCM-SHA256, ECDHE-*-AES256-GCM-SHA384, ECDHE-*-CHACHA20-POLY1305Forward secrecy + AEAD
Accept (legacy clients only)ECDHE-*-AES128-CBC-SHA256, DHE-RSA-AES128-GCM-SHA256 (≥ 2048-bit group)FS but CBC (timing-attack history) or slow/weak-group DHE
AvoidRSA-kex suites (AES128-GCM-SHA256, etc.), anything with CBC-SHA (SHA-1 HMAC) on new deploymentsNo forward secrecy; ROBOT-style oracles; Lucky13
NeverRC4, 3DES/DES, EXPORT, NULL, anon, MD5Broken: RC4 biases, Sweet32, FREAK/Logjam, no encryption, no authentication

TLS 1.3 needs no such table. It defines only five suites, all AEAD:

SuiteCodeUse
TLS_AES_128_GCM_SHA2560x1301Default everywhere; mandatory to implement
TLS_AES_256_GCM_SHA3840x1302Common; required by some compliance regimes
TLS_CHACHA20_POLY1305_SHA2560x1303Fast without AES hardware (older phones, embedded)
TLS_AES_128_CCM_SHA2560x1304IoT / constrained devices
TLS_AES_128_CCM_8_SHA2560x1305IoT only (short 8-byte tag); not for the web

Enumerating what a server supports

bash
nmap --script ssl-enum-ciphers -p 443 example.com        # graded list per protocol version
testssl.sh --protocols --ciphers --vulnerable example.com
openssl s_client -connect example.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'   # does this one work?
openssl s_client -connect example.com:443 -tls1_3 -ciphersuites TLS_CHACHA20_POLY1305_SHA256
Tip

In TLS 1.2 the server usually decides from the client's list (with "server cipher preference" enabled). Modern guidance (Mozilla "intermediate") lets the client pick among AEAD suites: a phone without AES hardware will choose ChaCha20, which is faster for it.


The Record Layer

What is this?Everything TLS sends — handshake messages, alerts, application data — is wrapped in records. The record layer splits data into chunks of up to 16 KB, protects each chunk with the current keys, and tags it with a content type.

TLS record header (5 bytes, always in the clear)
Content type1 B
Version2 B
Length2 B
Fragment≤ 16 KB
Content type
20 change_cipher_spec · 21 alert · 22 handshake · 23 application_data · 24 heartbeat
Version
0x0303 for TLS 1.2 and (frozen) for TLS 1.3; 0x0301 allowed in the first ClientHello
Length
Up to 2^14 (16,384) bytes of plaintext; up to 2^14 + 256 of ciphertext in TLS 1.3
Fragment
The payload: plaintext before keys exist, ciphertext + tag afterwards

Handshake messages have their own small header inside handshake records. One handshake message can span several records, and one record can carry several handshake messages.

Handshake message header (inside a content-type 22 record)
msg_type1 B
length3 B
bodyvar
msg_type
1 ClientHello · 2 ServerHello · 4 NewSessionTicket · 8 EncryptedExtensions · 11 Certificate · 12 ServerKeyExchange · 13 CertificateRequest · 14 ServerHelloDone · 15 CertificateVerify · 16 ClientKeyExchange · 20 Finished · 24 KeyUpdate
length
Length of the message body (up to 16 MB)
body
The message itself

What you'd see in a hex dump of a ClientHello:

16 03 01 02 00   01 00 01 fc   03 03   a3 4f 9c ... (32 random bytes)
│  │     │       │  │          │
│  │     │       │  │          └─ legacy_version 0x0303
│  │     │       │  └──────────── handshake length 0x0001fc = 508
│  │     │       └─────────────── msg_type 1 = ClientHello
│  │     └─────────────────────── record length 0x0200 = 512
│  └───────────────────────────── record version 0x0301 (compat value)
└──────────────────────────────── content type 0x16 = 22 = handshake

TLS 1.3 hides the real content type

TLS 1.3 protected record — outer header lies on purpose
Outer type1 B
Version2 B
Length2 B
Encrypted: contentvar
Encrypted: real type1 B
Encrypted: padding0+ B
AEAD tag16 B
Outer type
Always 23 (application_data) once keys are in use
Version
Always 0x0303
Length
Ciphertext length
Encrypted: content
Handshake, alert or application data
Encrypted: real type
The true content type, inside the encryption
Encrypted: padding
Optional zeros to hide the true length
AEAD tag
Authenticates everything (header bytes are the additional data)

The nonce for each record is the per-direction IV XORed with a 64-bit sequence number that both sides count but never send. Drop, replay or reorder a record and the tag check fails. This is TLS's anti-replay mechanism within a connection.

Alerts

Alerts are two bytes: level (1 warning, 2 fatal) and description. In TLS 1.3 everything except close_notify and user_canceled is fatal.

CodeAlertUsually means
0close_notifyClean shutdown. Missing it allows truncation attacks
10unexpected_messageState machine violation
20bad_record_macDecryption/tag failure: tampering or a key mismatch
40handshake_failureNo common parameters
42 / 45 / 48bad_certificate / certificate_expired / unknown_caPeer rejected the certificate
70protocol_versionNo common TLS version
71insufficient_securityOffered parameters too weak
86inappropriate_fallbackDowngrade attempt detected (TLS_FALLBACK_SCSV)
112unrecognized_nameNo certificate for that SNI
116certificate_requiredmTLS server got no client certificate (1.3)
120no_application_protocolALPN mismatch

The TLS 1.2 Handshake

With RSA key exchange (no forward secrecy)

sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello<br/>version, client random, suites, extensions
  S->>C: ServerHello<br/>chosen suite, server random, session ID
  S->>C: Certificate (RSA public key)
  S->>C: ServerHelloDone
  Note over C: Pick 48-byte<br/>pre-master secret,<br/>encrypt to RSA key
  C->>S: ClientKeyExchange (encrypted pre-master)
  C->>S: ChangeCipherSpec
  C->>S: Finished (encrypted)
  S->>C: ChangeCipherSpec
  S->>C: Finished (encrypted)
  Note over C,S: 2 round trips before application data

With ECDHE (forward secrecy)

sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello<br/>suites, supported_groups, sig algs
  S->>C: ServerHello (ECDHE_RSA suite)
  S->>C: Certificate
  S->>C: ServerKeyExchange<br/>curve + ephemeral public key<br/>signed with cert private key
  S->>C: ServerHelloDone
  Note over C: Verify signature,<br/>make own ephemeral<br/>key, derive secret
  C->>S: ClientKeyExchange (client ephemeral public key)
  C->>S: ChangeCipherSpec + Finished
  S->>C: ChangeCipherSpec + Finished

The signature in ServerKeyExchange is the authentication. It covers both randoms and the server's DH public key, so an attacker can't swap in their own DH key without the certificate's private key.

From secret to keys (TLS 1.2)

  1. Pre-master secretSecret

    RSA: 48 random bytes chosen by the client. ECDHE: the shared x-coordinate.

  2. Master secret = PRF(pre-master, "master secret", client_random + server_random)PRF48 bytes

    With the Extended Master Secret extension (RFC 7627) the seed is the session hash instead, which binds the master secret to the whole handshake (fixes the Triple Handshake attack).

  3. Key block = PRF(master, "key expansion", server_random + client_random)PRF

    Sliced into client/server MAC keys (CBC suites), client/server encryption keys, and client/server IVs.

  4. Finished = PRF(master, "client finished" or "server finished", hash(all handshake messages))Check12 bytes

    Proves both sides saw the same handshake and derived the same keys.

Session resumption

A full handshake costs 2 round trips plus public-key operations. Resumption reuses the master secret from an earlier session in an abbreviated handshake (1 round trip, no certificate, no key exchange).

Session IDs

The server keeps a cache of master secrets keyed by a session ID. The client sends the ID in its next ClientHello. Hard to scale across a server fleet (shared cache needed).

Session tickets (RFC 5077)

The server encrypts the session state with a ticket key only it knows, and hands the blob to the client. Stateless for the server, but the ticket key becomes a crown jewel: whoever steals it can decrypt every session resumed with it, which breaks forward secrecy until the key is rotated.

sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: ClientHello + session ticket (or session ID)
  S->>C: ServerHello (same session)
  S->>C: ChangeCipherSpec + Finished
  C->>S: ChangeCipherSpec + Finished
  Note over C,S: 1 RTT, no Certificate, no key exchange

Mutual TLS (client certificates)

sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello
  S->>C: ServerHello, Certificate, ServerKeyExchange
  S->>C: CertificateRequest<br/>acceptable CAs + sig algs
  S->>C: ServerHelloDone
  C->>S: Certificate (client cert, in the clear in 1.2)
  C->>S: ClientKeyExchange
  C->>S: CertificateVerify<br/>signature over handshake so far
  C->>S: ChangeCipherSpec + Finished
  S->>C: ChangeCipherSpec + Finished
Warning

In TLS 1.2 the client certificate is sent unencrypted, so a passive observer learns the client's identity (name, email, device ID). TLS 1.3 encrypts it. This matters for mTLS-based device identity and zero-trust deployments.

Renegotiation

TLS 1.2 allowed a new handshake inside an existing connection, used for "this URL needs a client certificate" or rekeying. It caused CVE-2009-3555 (see attacks below) and DoS issues. The renegotiation_info extension (RFC 5746) fixed the injection flaw. TLS 1.3 removed renegotiation entirely.


TLS Extensions

Extensions are typed fields at the end of ClientHello/ServerHello that let TLS grow without new versions. Each side ignores extension types it doesn't recognise, which is what makes them safe to add. A server may only send an extension the client offered.

Extension (code)What it doesSecurity note
server_name (0)SNI: which hostname the client wantsPlaintext before ECH; used by censors and firewalls
status_request (5)Ask the server to staple OCSPPrivacy-preserving revocation
supported_groups (10)Curves / DH groups offeredDropping weak groups stops Logjam-style downgrades
signature_algorithms (13)Which signature + hash combos are acceptablePrevents being forced into SHA-1 or PKCS#1 v1.5
application_layer_protocol_negotiation (16)ALPN: h2, http/1.1, …Prevents cross-protocol attacks (ALPACA) when enforced
signed_certificate_timestamp (18)Deliver CT SCTs in the handshakeAlternative to embedding SCTs in the cert
encrypt_then_mac (22)MAC the ciphertext, not the plaintext, in CBC suitesKills padding-oracle classes (RFC 7366)
extended_master_secret (23)Bind master secret to the session hashFixes Triple Handshake (RFC 7627)
session_ticket (35)Stateless resumptionTicket key compromise breaks FS
heartbeat (15)Keep-alive for DTLSThe extension Heartbleed lived in; disable it
renegotiation_info (65281)Bind renegotiations to the previous handshakeFix for CVE-2009-3555
SNI in practice

One IP can host thousands of HTTPS sites because the server reads SNI to pick the certificate. No SNI → default certificate → name mismatch. Firewalls and CDNs route and filter on SNI, and domain fronting abused the gap between SNI and the HTTP Host header.

OCSP stapling in practice

Configure the server (ssl_stapling on in nginx) and check with openssl s_client -status. Look for OCSP Response Status: successful.

Session tickets in practice

Rotate ticket keys often (hours), share them securely across the fleet, and never persist them to disk. A ticket key that lives for months quietly turns months of traffic into one decryptable archive.


Decrypting and Inspecting TLS

There are exactly two ways to read TLS traffic you captured: have the session keys, or be in the middle of the connection.

RSA private key (TLS 1.2, RSA kex only)

Load the server's private key into Wireshark; it decrypts each ClientKeyExchange to get the pre-master secret. Useless against ECDHE and TLS 1.3 — the private key never touched the session secret.

Key log file (any version)

Browsers, curl and most TLS libraries write per-session secrets when SSLKEYLOGFILE is set. Wireshark reads the file and decrypts everything, including TLS 1.3 and QUIC.

Active interception (MITM proxy)

A proxy terminates TLS with its own certificate (trusted via an installed root) and opens a second TLS connection to the real server. Used by corporate inspection, Burp, mitmproxy — and by malware.

Endpoint visibility

EDR or eBPF hooks read plaintext before encryption (e.g. uprobes on SSL_write). No crypto broken, but full visibility on that host.

text
# SSLKEYLOGFILE format (NSS key log) — one line per secret
CLIENT_RANDOM                    <client_random_hex> <master_secret_hex>      ← TLS 1.2
CLIENT_HANDSHAKE_TRAFFIC_SECRET  <client_random_hex> <secret_hex>             ← TLS 1.3
SERVER_HANDSHAKE_TRAFFIC_SECRET  <client_random_hex> <secret_hex>
CLIENT_TRAFFIC_SECRET_0          <client_random_hex> <secret_hex>
SERVER_TRAFFIC_SECRET_0          <client_random_hex> <secret_hex>
EXPORTER_SECRET                  <client_random_hex> <secret_hex>
bash
SSLKEYLOGFILE=$HOME/tls-keys.log curl -s https://example.com -o /dev/null
# Wireshark: Preferences → Protocols → TLS → (Pre)-Master-Secret log filename
# Useful display filters:
#   tls.handshake.type == 1                          ClientHellos
#   tls.handshake.extensions_server_name == "example.com"
#   tls.record.content_type == 21                    alerts
#   tls.handshake.ja4                                JA4 fingerprints (recent Wireshark)
Caution

A key log file is the session's crown jewels. Malware that sets SSLKEYLOGFILE system-wide gets silent decryption of every browser session. Hunt for that environment variable on endpoints, and never leave it set on a workstation after debugging.


TLS Defenses: HSTS, CAA and Certificate Transparency

HSTS (HTTP Strict Transport Security, RFC 6797)

http
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-age

How long (seconds) the browser must use HTTPS only for this host. Two years is the preload requirement.

includeSubDomains

Applies the policy to every subdomain. Dangerous if any internal subdomain can't do HTTPS.

preload

Consent to be hard-coded into browsers' preload list (hstspreload.org), which removes the first-visit gap.

Hard-fail

Under HSTS, certificate errors have no "proceed anyway" button: users can't click through a MITM warning.

HPKP (HTTP Public Key Pinning, RFC 7469) tried to go further by pinning keys, but sites bricked themselves and attackers could pin hostile keys ("ransom pinning"). Chrome removed it in 2018–2019. Certificate Transparency replaced pinning as the web's defence against rogue certificates.

CAA (Certification Authority Authorization, RFC 8659)

A DNS record that says which CAs may issue for your domain. Since September 2017 every public CA must check it before issuing.

text
example.com.  CAA 0 issue     "letsencrypt.org"                 ; only Let's Encrypt may issue
example.com.  CAA 0 issuewild ";"                               ; nobody may issue wildcards
example.com.  CAA 0 iodef     "mailto:security@example.com"     ; report violations here
example.com.  CAA 0 issue     "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123"   ; RFC 8657: only *our* account

CAA is a check on honest CAs, not on attackers: a compromised CA can ignore it. Combine it with CT monitoring.

Certificate Transparency (RFC 6962, v2 RFC 9162)

What is this?A set of public, append-only logs that every publicly trusted certificate must be submitted to. Anyone can search them, so a domain owner can see every certificate ever issued for their names. Browsers refuse certificates that can't prove they were logged.

How a certificate gets logged before you ever see it
  1. CA builds a precertificateCA

    The final certificate plus a "poison" extension that makes it unusable for TLS.

  2. Submit to several independent logsCT logs

    Each log returns an SCT (Signed Certificate Timestamp): a signed promise to add it within the Maximum Merge Delay (24 h).

  3. Embed the SCTs and issue the real certificateCA

    SCTs can also be delivered via the TLS extension or a stapled OCSP response.

  4. Browser checks the SCTsBrowser

    Valid signatures from logs it trusts, from distinct operators. Chrome requires 2 or 3 depending on lifetime.

  5. Monitors watch the logsMonitors

    Domain owners (or services such as crt.sh, Cloudflare, Facebook) alert on unexpected certificates for their names.

  6. Auditors check the logs are honestAuditors

    Inclusion proofs (is this cert in the tree?) and consistency proofs (did the log only append?).

Why a Merkle tree?The log is a binary hash tree. Each leaf is a hash of one certificate, and each parent is the hash of its two children. The root hash (signed as the Signed Tree Head, STH) commits to every entry.

text
                    Root = H(H12 ‖ H34)          ← signed tree head (STH)
                   /                   \
           H12 = H(H1 ‖ H2)      H34 = H(H3 ‖ H4)
            /        \             /        \
         H1          H2         H3          H4
       cert A      cert B     cert C      cert D

Inclusion proof for cert C: give the client H4 and H12.
  Client computes H3 = hash(C) → H34 = H(H3 ‖ H4) → Root = H(H12 ‖ H34), compares with the signed STH.
  Proof size = log2(n) hashes: ~20-30 hashes even for a billion certificates.

Consistency proof: shows tree of size 4 is a prefix of tree of size 8, i.e. the log only appended
and never rewrote history.
Memory hook

"CT doesn't prevent mis-issuance; it makes it impossible to hide." A rogue CA can still sign a certificate for your domain. But to be accepted by browsers it has to be logged publicly, and your monitor fires within hours. DigiNotar (2011) went undetected for weeks. Under CT, the same attack is visible to the world.


Major TLS Attacks and Failures

Each of these broke something specific, and each fix is now a rule in modern configs.

YearAttackCVEWhat brokeFix
2009Insecure renegotiationCVE-2009-3555Attacker's data spliced before the victim's renegotiated sessionrenegotiation_info (RFC 5746); removed in 1.3
2011BEASTCVE-2011-3389Predictable CBC IVs in TLS 1.0 let a chosen-plaintext attacker decrypt cookiesTLS 1.1+ explicit IVs, 1/n-1 record splitting
2012CRIMECVE-2012-4929TLS compression leaked secrets through ciphertext lengthDisable TLS compression (removed in 1.3)
2013BREACHCVE-2013-3587Same idea via HTTP-level gzipDon't compress secrets with attacker input; token masking
2013Lucky13CVE-2013-0169Timing side channel in CBC MAC-then-encrypt padding checksConstant-time code; AEAD suites
2013–15RC4 biases—Statistical keystream biases recover plaintextRC4 prohibited (RFC 7465)
2014HeartbleedCVE-2014-0160OpenSSL heartbeat over-read leaked up to 64 KB of memory: keys, cookiesPatch, revoke and reissue certificates, rotate keys
2014POODLECVE-2014-3566SSL 3.0 CBC padding not checked; downgrade dance forced SSL 3.0Disable SSL 3.0; TLS_FALLBACK_SCSV
2014Triple Handshake—Resumption + renegotiation let a MITM sync master secrets across connectionsExtended Master Secret (RFC 7627)
2015FREAKCVE-2015-0204Clients accepted 512-bit export RSA when not askedRemove export suites
2015LogjamCVE-2015-4000Downgrade to 512-bit export DHE; precomputation on common 1024-bit groups≥ 2048-bit DH groups, prefer ECDHE
2016DROWNCVE-2016-0800SSLv2 on any server sharing the RSA key enabled decryption of TLS sessionsDisable SSLv2 everywhere; don't share keys
2016Sweet32CVE-2016-218364-bit block ciphers (3DES) collide after ~32 GB (birthday bound)Remove 3DES
2017ROBOTCVE-2017-13099 et al.Bleichenbacher padding oracle in RSA key exchange, still alive 19 years laterDisable RSA key exchange
2020RaccoonCVE-2020-1968Timing leak from leading-zero stripping of DH secretsEphemeral, never-reused DH keys; TLS 1.3
2021ALPACA—Cross-protocol: redirect HTTPS traffic to an FTP/SMTP server sharing the certificateStrict ALPN and SNI checking

Insecure renegotiation, step by step

sequenceDiagram
  participant V as Victim client
  participant A as Attacker (MITM)
  participant S as Server
  A->>S: Full TLS handshake (as the attacker)
  A->>S: "GET /transfer?to=attacker" + "X-Ignore: " (no newline)
  V->>A: ClientHello (victim starts TLS)
  A->>S: Forwards it as a RENEGOTIATION in the attacker's session
  S-->>V: Handshake completes, encrypted end to end with the victim
  V->>S: "GET /home" + victim's cookie
  Note over S: Server sees ONE<br/>request: attacker's<br/>line + victim's<br/>cookie

The server glued the attacker's plaintext prefix onto the victim's authenticated request, because nothing tied the renegotiated handshake to the connection it happened inside. RFC 5746 fixed this by making each renegotiation include the previous handshake's Finished values.

Lesson 1 — options are attack surface

Compression, renegotiation, export suites, SSLv2 compatibility, heartbeat: almost every major attack lived in a feature most deployments didn't need.

Lesson 2 — downgrades are the real enemy

FREAK, Logjam, POODLE and DROWN all forced a connection onto the weakest thing both sides still supported. Removing weak options beats negotiating safely around them.

Lesson 3 — MAC-then-encrypt is fragile

Padding oracles and timing leaks kept recurring until AEAD made encryption and authentication one operation.

Lesson 4 — implementation bugs beat protocol bugs

Heartbleed needed no cryptanalysis, just a missing bounds check. Memory-safe TLS stacks (rustls, Go) are partly a response.


TLS 1.3: What Changed and Why

What is this?TLS 1.3 (RFC 8446, 2018) is less an upgrade than a cleanup. It removes every feature that produced a major attack, makes forward secrecy mandatory, encrypts most of the handshake, and cuts a full handshake to one round trip.

Removed

RSA key exchange, static DH, CBC, RC4, 3DES, SHA-1/MD5 in signatures, compression, renegotiation, custom DHE groups, ChangeCipherSpec, DSA, export suites.

Mandatory forward secrecy

Full handshakes always use (EC)DHE. Resumption can still use (EC)DHE with the PSK (psk_dhe_ke).

Encrypted handshake

Everything after ServerHello is encrypted, including the server certificate and (for mTLS) the client certificate.

1-RTT, 0-RTT

The client sends its key share in the first message. Resumed sessions can send data in the first flight (0-RTT).

New key derivation

HKDF-based key schedule with separate secrets per phase, replacing the 1.2 PRF.

Signed transcript

CertificateVerify signs a hash of the entire handshake, not just the key exchange parameters (1.2's weakness exploited by Logjam-style attacks).

TLS 1.2 vs TLS 1.3

FeatureTLS 1.2TLS 1.3
Handshake RTT2 (1 with resumption)1 (0 with 0-RTT)
Forward secrecyOptionalMandatory (full handshakes)
Cipher suites300+ defined (including weak)5, all AEAD
Suite name coversKex + auth + cipher + hashCipher + hash only
Certificate in handshakePlaintextEncrypted
Client certificate (mTLS)PlaintextEncrypted
RSA key transportAllowedRemoved
RC4, 3DES, CBC, export ciphersAllowedRemoved
Compression / renegotiationAllowedRemoved (KeyUpdate + post-handshake auth instead)
Downgrade protectionWeak (Finished only, SCSV)Transcript signature + ServerHello.random sentinel
Key derivationPRF(master_secret)HKDF key schedule
Session resumptionSession IDs / ticketsPSK (tickets carry PSK identities)
Record content typePlaintextHidden inside encryption
Memory hook

TLS 1.3 is 1-RTT because the client "guesses" and is almost always right. In TLS 1.2 the client first asks which key-exchange to use, waits, then sends its key — two round trips of negotiating. TLS 1.3 collapses this: there's only one key-exchange family (ECDHE), so the client just sends its ECDHE key share in the very first message (the ClientHello) without asking. The server replies with its own share and is already able to encrypt the certificate. Mnemonic: 1.2 negotiates-then-acts; 1.3 acts immediately because there's nothing left to negotiate. That same "send key share upfront" is also why the certificate is now encrypted (a passive eavesdropper can't see which site you visited from the cert) and why forward secrecy is automatic (ECDHE every time, RSA key transport deleted).

The full handshake

sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello<br/>supported_versions, key_share,<br/>signature_algorithms, SNI, ALPN
  S->>C: ServerHello<br/>key_share, supported_versions
  Note over C,S: Handshake keys derived — all below is encrypted
  S->>C: EncryptedExtensions (ALPN, ...)
  S->>C: CertificateRequest (only for mTLS)
  S->>C: Certificate
  S->>C: CertificateVerify
  S->>C: Finished
  Note over S: May already send<br/>data (0.5-RTT)
  C->>S: Certificate + CertificateVerify (only for mTLS)
  C->>S: Finished
  Note over C,S: Application traffic keys in use
  S-->>C: NewSessionTicket (any time after)

When the client guessed wrong: HelloRetryRequest

If the client's key share is for a group the server won't use (say the client sent only X25519MLKEM768 and the server only does P-256), the server replies with a HelloRetryRequest. It's a ServerHello whose random field is the fixed value SHA-256("HelloRetryRequest"). The cost is one extra round trip.

sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: ClientHello, key_share X25519MLKEM768
  S->>C: HelloRetryRequest<br/>"use secp256r1" + cookie
  C->>S: ClientHello again<br/>key_share secp256r1 + cookie
  S->>C: ServerHello ... Finished
  C->>S: Finished

The cookie extension lets the server stay stateless during the retry (useful against DoS, and essential in DTLS/QUIC).

Renegotiation is gone — what replaced it

KeyUpdate

Either side can send a KeyUpdate message to roll its sending keys forward (traffic_secret_N+1 = HKDF-Expand-Label(secret_N, "traffic upd", "", L)). Used for long-lived connections; no new handshake.

Post-handshake authentication

If the client advertised post_handshake_auth, the server can send a CertificateRequest later in the connection (e.g. when the user reaches /admin). HTTP/2 forbids it, so in practice it's used with HTTP/1.1.

Middleboxes, GREASE and the version number that lies

When TLS 1.3 was tested on the real internet, a few percent of connections failed because firewalls and inspection boxes choked on anything unfamiliar. The fix was to make TLS 1.3 look like a TLS 1.2 session resumption:

Frozen version fields

Record version and legacy_version stay 0x0303 (TLS 1.2). The real version is negotiated in the supported_versions extension.

Fake session ID

The client sends a random 32-byte legacy_session_id and the server echoes it, as in 1.2 resumption.

Dummy ChangeCipherSpec

Both sides may send a meaningless CCS record, which middleboxes expect to see (RFC 8446 appendix D.4, "middlebox compatibility mode").

GREASE (RFC 8701)

Clients sprinkle random reserved values into cipher, extension, group and version lists. Servers that crash on unknown values get caught now, so the protocol stays extensible ("don't let the joints rust").

Downgrade protectionA TLS 1.3 server forced down to TLS 1.2 puts the bytes 44 4F 57 4E 47 52 44 01 ("DOWNGRD\x01") at the end of ServerHello.random (...00 for TLS 1.1 or lower). The random is covered by the signature, so an attacker can't remove it, and a 1.3-capable client that sees it aborts.

Forward secrecy and the visibility debate

Mandatory forward secrecy broke a long-standing enterprise practice: passive decryption by loading the server's RSA private key into a monitoring appliance. With ECDHE there's nothing to load. Organisations now choose between:

Terminate and re-encrypt

A load balancer or proxy ends TLS, inspects, and starts a new TLS connection. The usual answer.

Key export

Servers log session secrets (key log format) to a secure collector. Works, but key handling is now your problem.

Endpoint telemetry

EDR / eBPF reads plaintext on the host.

"Visibility" variants

ETSI's Enterprise Transport Security (ETS, formerly eTLS) reuses a static DH key to restore passive decryption. The IETF rejected the idea; it deliberately removes forward secrecy.

Decrypting TLS 1.3

The only options are the key log file or being an active MITM. The RSA-private-key trick does not work, not even with an RSA certificate, because RSA only signs in TLS 1.3.


TLS 1.3 Under the Hood

The key schedule

TLS 1.3 derives every key from three chained secrets using HKDF (RFC 5869). HKDF-Extract concentrates entropy into a fixed-size secret. HKDF-Expand-Label stretches a secret into labelled outputs (all labels are prefixed with "tls13 "). Each stage mixes in new input and hashes of the handshake transcript so far.

        PSK (or zeros)
             │
  0 ──▶ HKDF-Extract ══▶ EARLY SECRET ──┬──▶ binder_key
             │                          ├──▶ client_early_traffic_secret     ← 0-RTT
             │                          └──▶ early_exporter_master_secret
             ▼
      Derive-Secret("derived")
             │
 (EC)DHE ─▶ HKDF-Extract ══▶ HANDSHAKE SECRET ──┬──▶ client_handshake_traffic_secret
             │                                  └──▶ server_handshake_traffic_secret
             ▼
      Derive-Secret("derived")
             │
  0 ──▶ HKDF-Extract ══▶ MASTER SECRET ──┬──▶ client_application_traffic_secret_0
                                         ├──▶ server_application_traffic_secret_0
                                         ├──▶ exporter_master_secret
                                         └──▶ resumption_master_secret
SecretHKDF labelTranscript hashed inProtects
binder_keyext binder / res binder(empty)PSK binder in the ClientHello
client_early_traffic_secretc e trafficClientHello0-RTT early data
client / server handshake trafficc hs traffic / s hs trafficClientHello … ServerHelloEncryptedExtensions → Finished
client / server application trafficc ap traffic / s ap trafficClientHello … server FinishedAll application data
exporter_master_secretexp masterClientHello … server FinishedKeying material for applications
resumption_master_secretres masterClientHello … client FinishedPSKs in future NewSessionTickets

Derive-Secret(Secret, Label, Messages) = HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.length), and every label is prefixed with "tls13 " on the wire.

From a traffic secret to bytes on the wire
  1. write_key = HKDF-Expand-Label(traffic_secret, "key", "", key_length)Keys

    16 bytes for AES-128-GCM, 32 for AES-256-GCM / ChaCha20.

  2. write_iv = HKDF-Expand-Label(traffic_secret, "iv", "", 12)Keys
  3. per-record nonce = write_iv XOR (64-bit sequence number, left-padded)Nonce

    Unique for every record without sending anything; the sequence number resets to 0 when keys change.

  4. AEAD-Encrypt(write_key, nonce, additional_data = record header, plaintext = content ‖ type ‖ padding)Seal

What the signatures and MACs cover

CertificateVerify

Signature over: 64 bytes of 0x20, the string "TLS 1.3, server CertificateVerify", a 0x00 byte, then the transcript hash up to the Certificate. The fixed prefix stops a signature from being reused in another context (or by TLS 1.2).

Finished

finished_key = HKDF-Expand-Label(handshake_traffic_secret, "finished", "", Hash.length), then verify_data = HMAC(finished_key, transcript hash). Proves key possession and that both sides saw identical messages.

Exporters

exporter_master lets applications derive extra keying material bound to the session (RFC 5705/8446), e.g. for token binding or channel binding in authentication protocols.

Session resumption with PSKs

After the handshake the server sends one or more NewSessionTicket messages. Each ticket identifies a PSK derived from resumption_master_secret. The ticket blob itself is opaque to the client: usually the server's encrypted session state.

NewSessionTicket message
ticket_lifetime4 B
ticket_age_add4 B
ticket_noncevar
ticketvar
extensionsvar
ticket_lifetime
Seconds the ticket may be used; at most 604,800 (7 days)
ticket_age_add
Random value added to the client's reported ticket age, so passive observers can't link resumptions by age
ticket_nonce
Makes each ticket's PSK unique: PSK = HKDF-Expand-Label(resumption_master, "resumption", nonce, L)
ticket
The opaque identity the client sends back (often server-encrypted state)
extensions
e.g. early_data with max_early_data_size (0-RTT allowed)
sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello<br/>key_share + psk_key_exchange_modes psk_dhe_ke<br/>pre_shared_key (ticket identity + binder)
  S->>C: ServerHello<br/>pre_shared_key (selected) + key_share
  S->>C: EncryptedExtensions
  S->>C: Finished
  C->>S: Finished
  Note over C,S: No Certificate / CertificateVerify<br/>PSK proves identity, (EC)DHE keeps forward secrecy
psk_ke

Resume with the PSK alone. Cheapest, but the session keys depend only on the PSK: no forward secrecy for the resumed session if the ticket key leaks.

psk_dhe_ke

PSK + a fresh (EC)DHE exchange. Forward secrecy preserved. What browsers use.

Binder

An HMAC over the ClientHello (minus the binders) keyed from the PSK. Proves the client really holds the PSK and binds it to this exact hello. pre_shared_key must be the last extension because of it.

0-RTT early data

A returning client can encrypt application data with client_early_traffic_secret (derived from the PSK) and send it in the same flight as the ClientHello.

sequenceDiagram
  autonumber
  participant C as Client
  participant S as Server
  C->>S: ClientHello + early_data + pre_shared_key
  C->>S: Early data: GET /home (0-RTT keys)
  S->>C: ServerHello + EncryptedExtensions (early_data accepted)
  S->>C: Finished
  S-->>C: Response to GET /home (0.5-RTT)
  C->>S: EndOfEarlyData + Finished
Caution

0-RTT data can be replayedIt's encrypted under keys derived only from the PSK and the ClientHello, with no fresh server input. An attacker can capture that first flight and send it again, to the same server or another node in the fleet. It also has no forward secrecy against PSK compromise.

Single-use tickets

The server remembers which tickets were used and rejects repeats. Hard across a global fleet.

ClientHello recording

Remember recent ClientHello randoms within a time window and reject duplicates.

Freshness check

Reject early data whose obfuscated ticket age doesn't match expectations (limits the replay window).

Application rules

Only accept 0-RTT for safe, idempotent requests (GET without side effects). CDNs mark requests with Early-Data: 1, and origins can answer 425 Too Early (RFC 8470) to force a retry after the handshake.

Mutual TLS in 1.3

The server's CertificateRequest comes inside the encrypted flight, and the client's Certificate + CertificateVerify are encrypted too, so a passive observer no longer learns client identities. The request can carry signature_algorithms, certificate_authorities (acceptable CAs) and oid_filters (required certificate extensions). A client with no suitable certificate sends an empty Certificate; the server decides whether to abort (certificate_required, alert 116).


TLS 1.3 Extensions

In TLS 1.3, extensions carry most of the negotiation, and each extension may only appear in specific messages: CH (ClientHello), SH (ServerHello), EE (EncryptedExtensions), CT (Certificate), CR (CertificateRequest), NST (NewSessionTicket), HRR (HelloRetryRequest).

Extension (code)Appears inPurpose
supported_versions (43)CH, SH, HRRThe real version negotiation (0x0304)
key_share (51)CH, SH, HRR(EC)DHE public values; HRR names the group the server wants
signature_algorithms (13)CH, CRAlgorithms acceptable for CertificateVerify signatures
signature_algorithms_cert (50)CH, CRAlgorithms acceptable for signatures inside certificates (if different)
supported_groups (10)CH, EEGroups the client supports (server may list its preferences in EE)
pre_shared_key (41)CH, SHPSK identities and binders; must be last in CH
psk_key_exchange_modes (45)CHpsk_ke or psk_dhe_ke
early_data (42)CH, EE, NSTRequest / accept 0-RTT; max size in NST
cookie (44)CH, HRRStateless HelloRetryRequest; DoS protection
server_name (0)CH, EESNI (EE carries an empty ack)
application_layer_protocol_negotiation (16)CH, EEALPN, now encrypted in the server's answer
status_request (5)CH, CR, CTOCSP staple now rides per certificate in the Certificate message
signed_certificate_timestamp (18)CH, CR, CTSCTs per certificate entry
post_handshake_auth (49)CHClient allows a later CertificateRequest
certificate_authorities (47)CH, CRHint which CAs are acceptable
encrypted_client_hello (0xfe0d)CH, EE, HRRECH: the real ClientHello encrypted to the server's public key
signature_algorithms vs signature_algorithms_cert

The first governs the live handshake signature (proof of key possession). The second governs what CA signatures the client can verify in the chain. A client might sign-check RSA-PSS in the handshake while accepting PKCS#1 v1.5 signatures in certificates.

Cookie

In a HelloRetryRequest the server can pack its state into a cookie, forget the connection, and rebuild state when the client echoes it. A return-routability check: spoofed source addresses never see the cookie.

ALPN

The client offers h2, http/1.1 (or h3 in QUIC); the server picks one in EncryptedExtensions. Strict ALPN enforcement is a defence against cross-protocol attacks (ALPACA).

Post-handshake auth

Lets a server require a client certificate only for sensitive paths, without TLS 1.2-style renegotiation. Not allowed in HTTP/2, so modern designs authenticate the client up front or at the application layer.

ECH

The client fetches an ECHConfig from the HTTPS DNS record, encrypts its real ClientHello (with the real SNI) to that key, and wraps it in an outer ClientHello naming a shared front-end. Clients without a config send GREASE ECH so that real ECH traffic doesn't stand out.


TLS Fingerprinting (JA3 / JA4)

Last verified2026-06

What is this?When a client opens a TLS connection, its very first message — the ClientHello — advertises how it wants to talk: which TLS version, which cipher suites, which extensions, which elliptic-curve groups, and so on. Different software builds that list in its own distinctive order. A Python requests script, Chrome, and a piece of malware each produce a recognisably different ClientHello. TLS fingerprinting turns that ClientHello into a short hash so you can identify the client software without decrypting anything.

The key point: the ClientHello is sent in the clear, before encryption starts (it has to be — it's negotiating the encryption). So a network sensor that can't read your encrypted traffic can still read the fingerprint. You're identifying the tool, not the content.

Why it mattersEncryption blinds the defender to payload, but the handshake metadata still leaks. A real browser and a malware C2 client both speak TLS, but they build their ClientHello differently — so the fingerprint distinguishes "Chrome on macOS" from "Cobalt Strike's default profile" or "a Go program" even though both connections are encrypted. It's a top-of-Pyramid-of-Pain signal: it targets the attacker's tooling, not an IP or hash they can trivially change.

How it works

JA3 (client):  hash of  TLSVersion,Ciphers,Extensions,EllipticCurves,ECPointFormats
               taken from the ClientHello, joined with "-" and "," then MD5'd.

  ClientHello fields ──▶ "771,4865-4866-...,0-23-65281-10-11-...,29-23-24,0"
                     ──▶ MD5 ──▶ e7d705a3286e19ea42f587b344ee6865

JA3S (server): the same idea applied to the ServerHello (version, chosen cipher,
               extensions). JA3 + JA3S together fingerprint *both ends* of a session.
JA3

was published by Salesforce in 2017. Its weakness: it MD5s an ordered list, and TLS 1.3 clients are allowed to shuffle/pad extensions (GREASE values, the padding extension), which makes the raw JA3 unstable and easy to randomise — so a single piece of software can emit many JA3 hashes, and attackers deliberately "JA3-randomise."

JA4

(and the JA4+ family, released by FoxIO in 2023) is the modern successor. It fixes JA3's fragility by sorting the cipher and extension lists before hashing (so GREASE/reordering doesn't change the result) and by using a human-readable, structured format instead of one opaque MD5. A JA4 fingerprint looks like t13d1516h2_8daaf6152771_b186095e22b6 — the first chunk is readable (t=TCP, 13=TLS 1.3, d=domain/SNI present, counts of ciphers/extensions, ALPN), the rest are truncated hashes of the sorted lists. JA4+ extends the idea to other protocols: JA4H (HTTP), JA4S (server response), JA4L (latency/location), JA4X (X.509 certificate).

JA4 fingerprint, decoded:
  t 13 d 15 16 h2 _ 8daaf6152771 _ b186095e22b6
  │  │ │  │  │  │       │              │
  │  │ │  │  │  │       │              └─ hash of sorted extensions
  │  │ │  │  │  │       └──────────────── hash of sorted cipher suites
  │  │ │  │  │  └─ ALPN (h2 = HTTP/2)
  │  │ │  │  └──── extension count (16)
  │  │ │  └─────── cipher count (15)
  │  │ └────────── SNI present (d = domain)
  │  └──────────── TLS version (13 = TLS 1.3, read from the supported_versions
  │                extension — not the legacy record version, which 1.3 pins to 1.2)
  └─────────────── transport (t = TCP, q = QUIC)

Security angle

Threat hunting / detection

maintain a set of fingerprints for known-bad tooling (Cobalt Strike, Sliver, Metasploit, common Go/Python malware stacks) and alert on them, or invert it — baseline the fingerprints normal in your environment and hunt the rare ones. A ClientHello claiming to be "Chrome" (by User-Agent) whose JA4 matches a Python client is a classic lie-detector for malware and scrapers.

Bot / scraper defence

the same trick spots automated clients that forge a browser User-Agent but can't forge the TLS stack underneath — a high-value signal for a consumer app fighting credential-stuffing and scraping. (This is why some bot frameworks now ship full browser TLS stacks specifically to match a real JA3/JA4.)

Evasion / limits

JA3 is easy to randomise, which is exactly why JA4's sorting matters. Even JA4 only identifies the stack, not intent — a benign tool and malware that both use Go's standard library share a fingerprint, so treat it as one pivot, not a verdict. Combine it with destination, beaconing regularity, and JA3S/JA4S on the server side.


Hash-based MAC (HMAC)

HMAC(key, message) = Hash((key XOR opad) ∥ Hash((key XOR ipad) ∥ message))
  • Prevents length extension attacks (which plain Hash(key ∥ message) is vulnerable to)
  • Security depends entirely on key secrecy (not on collision resistance of underlying hash)
  • HMAC-SHA256 is the current standard (used in HKDF, JWT HS256, AWS request signing)

Key Derivation Functions (KDF)

KDFUse CaseHow
HKDF (RFC 5869)TLS 1.3, WireGuardExtract entropy then expand
PBKDF2Password hashing (legacy)HMAC-based; iteration count for cost
bcryptPassword hashingBlowfish-based; work factor
Argon2idPassword hashing (recommended)Memory-hard + time-hard
scryptPassword hashing / disk encryptionMemory-hard

Why memory-hard functions matterGPU/ASIC attacks on simple iteration (PBKDF2) are viable because parallel hardware is cheap. Memory-hard functions require large memory per hash, limiting parallelism.


Quantum Threat and Post-Quantum Cryptography

Grover's algorithmSpeeds up brute-force search. Halves effective key length.

  • AES-128 → 64-bit security (borderline)
  • AES-256 → 128-bit security (still safe)
  • SHA-256 → 128-bit collision resistance (safe)

Shor's algorithmEfficiently solves integer factoring and ECDLP.

  • Breaks RSA, DH, ECDH, ECDSA completely when large-scale quantum computer exists

NIST Post-Quantum Standards (2024)

AlgorithmTypeStandard
CRYSTALS-Kyber (ML-KEM)Key encapsulation (KEM)FIPS 203
CRYSTALS-Dilithium (ML-DSA)Digital signatureFIPS 204
SPHINCS+ (SLH-DSA)Hash-based signatureFIPS 205

In TLS todaythe hybrid group X25519MLKEM768 (classical X25519 combined with ML-KEM-768) is on by default in Chrome (since Chrome 131, November 2024), Firefox, Safari and major CDNs, so a large and growing share of web traffic is already protected against "harvest now, decrypt later". It's a hybrid on purpose: the session stays secure as long as either component is unbroken. The key share is ~1.2 KB, which pushes the ClientHello past one packet and has broken a few middleboxes. Post-quantum signatures (ML-DSA) in certificates are not yet deployed on the public web: certificate chains would grow by several kilobytes.


Quick Reference: Where Algorithms Are Used

ContextAlgorithm
TLS 1.3 bulk encryptionAES-256-GCM or ChaCha20-Poly1305
TLS 1.3 key exchangeECDHE (X25519 or P-256)
TLS certificate signatureECDSA-P256 or RSA-PSS-2048
TLS HKDFHMAC-SHA384
SSH bulk encryptionChaCha20-Poly1305 or AES-GCM
SSH key exchangeECDH (curve25519)
SSH host keyEd25519
GPGRSA-4096 or Ed25519
HTTPS HSTS preloadSHA-256 pinning
JWT HS256HMAC-SHA256
JWT RS256RSA-PSS
JWT ES256ECDSA-P256
Bitcoinsecp256k1 ECDSA
Signal ProtocolDouble Ratchet (X25519 + AES-256-CBC + HMAC-SHA256)
WireGuardChaCha20-Poly1305 + Curve25519 + BLAKE2s
FileVault / BitLockerAES-XTS-256
LUKSAES-XTS-256
Password storageArgon2id (OWASP recommended)
AWS request signingHMAC-SHA256

Interview Questions: Encryption & Cryptography

Q
What's the difference between AES-CBC and AES-GCM, and why prefer GCM?
Model answer

CBC only provides confidentiality — it chains blocks with XOR and needs a random IV, but it has no built-in integrity, so it's typically paired with a separate HMAC and is historically prone to padding-oracle attacks if implemented carelessly. GCM is an AEAD mode: it encrypts with counter mode and produces an authentication tag in the same pass, so it gives confidentiality and integrity together, and it can authenticate associated data that isn't encrypted. You prefer GCM because it's authenticated by design, hardware-accelerated, and removes the encrypt-then-MAC footguns of CBC. The one rule with GCM is never reuse a nonce with the same key.

Q
What happens if you reuse a nonce with AES-GCM?
Model answer

It's catastrophic. GCM derives a keystream from the key and nonce, so two messages under the same key and nonce are XORed against the same keystream — meaning the XOR of the two ciphertexts equals the XOR of the two plaintexts, leaking content. Worse, nonce reuse also lets an attacker recover the GHASH authentication subkey, which breaks integrity and lets them forge valid tags for arbitrary messages. So a single nonce reuse compromises both confidentiality and authenticity. The fix is a unique nonce per encryption — a random 96-bit nonce or a non-wrapping counter — or AES-GCM-SIV if reuse is a real risk, since it's nonce-misuse-resistant.

Q
Why does TLS 1.3 always have forward secrecy but TLS 1.2 might not?
Model answer

Forward secrecy means a future compromise of the server's long-term private key can't decrypt past sessions, and it requires ephemeral key exchange. TLS 1.2 allowed ephemeral ECDHE but also still permitted RSA key transport, where the client encrypts the session secret to the server's RSA public key — if that key later leaks, all recorded sessions decrypt, so no forward secrecy. TLS 1.3 removed RSA key transport entirely and mandates ephemeral ECDHE for every handshake, so a fresh, discarded key pair protects each session by design. So it's not that 1.2 can't have forward secrecy — it's that 1.2 made it optional and 1.3 made it the only option.

Q
Explain the TLS 1.3 handshake and why it's 1-RTT.
Model answer

Because TLS 1.3 only supports ephemeral ECDHE key exchange, there's nothing to negotiate about how to exchange keys, so the client sends its ECDHE key share immediately in the ClientHello rather than first asking the server. The server responds with its own key share in the ServerHello, and at that point both sides can derive the shared secret via HKDF, so the server already encrypts the rest — certificate, CertificateVerify, and Finished. The client verifies and sends its Finished, and data flows after one round trip. As a bonus, sending the key share upfront is what lets the certificate be encrypted, hiding it from passive observers, and there's an optional 0-RTT mode that sends early data with the first flight at the cost of replay risk.

Q
Why is ECDSA vulnerable to k-reuse but Ed25519 isn't?
Model answer

ECDSA needs a fresh random nonce k for every signature, and the math is unforgiving: if k repeats across two signatures with the same key, the two signature equations share unknowns and the private key can be solved directly — this is how the PS3 was hacked and how Bitcoin keys have been stolen. Even a slightly biased or predictable k leaks the key over many signatures. Ed25519 eliminates the dependency by deriving k deterministically — it hashes the message together with the private key to produce the nonce — so there's no RNG to fail or repeat, and identical messages produce identical signatures without ever exposing the key. So Ed25519 removes the single most dangerous implementation pitfall of ECDSA.

Q
What's the difference between ECDH and ECDHE, and why does it matter?
Model answer

ECDH is elliptic-curve Diffie-Hellman key exchange; the E on the end, ECDHE, means ephemeral — a fresh key pair generated for each session and thrown away afterward. The distinction matters for forward secrecy. With static ECDH, the same long-term key derives every session secret, so compromising it later decrypts all past traffic. With ECDHE, each session's secret comes from throwaway keys that no longer exist, so a future key compromise can't unlock recorded sessions. That's why modern TLS insists on ECDHE. Mnemonic: the extra E is for ephemeral, and ephemeral is what buys forward secrecy.

Q
Why is Argon2id preferred over bcrypt for new password hashing?
Model answer

Both are deliberately slow, which is the point for password hashing, but bcrypt is only CPU-hard and uses a small fixed amount of memory, so attackers can parallelize it cheaply on GPUs and ASICs. Argon2id is memory-hard — it forces each guess to consume a configurable, significant amount of RAM, which makes massive parallel cracking far more expensive — and the id variant combines resistance to both GPU attacks and side-channel attacks. It also won the Password Hashing Competition and is OWASP's current recommendation. bcrypt is still acceptable, but for new deployments Argon2id raises the attacker's cost more, especially against well-funded hardware. Both must be salted.

Q
Explain Certificate Transparency and why DigiNotar prompted it.
Model answer

Certificate Transparency requires every publicly trusted certificate to be recorded in public, append-only logs, and browsers expect proof of logging, so any issued certificate becomes visible to the world. Domain owners can monitor the logs and detect certificates issued for their domains that they didn't request. The driver was DigiNotar in 2011: that CA was hacked and issued a fraudulent wildcard certificate for Google, which was used to intercept the traffic of hundreds of thousands of users in Iran, and nobody could detect it because mis-issuance was invisible. CT makes that kind of secret mis-issuance immediately detectable, and DigiNotar itself was distrusted and collapsed. The trade-off is that CT publicly exposes all your subdomains.

Q
What does Shor's algorithm break, and what does Grover's break?
Model answer

Shor's algorithm efficiently solves integer factorization and the discrete logarithm problem on a large quantum computer, which completely breaks the asymmetric primitives we rely on — RSA, Diffie-Hellman, ECDH, and ECDSA — because their security rests on exactly those hard problems. Grover's algorithm is a generic search speedup that effectively halves the security of symmetric primitives, so AES-128 drops to about 64-bit security (borderline) while AES-256 stays at a safe 128-bit, and hash collision resistance is similarly halved. The practical implication is that symmetric crypto just needs bigger keys, but asymmetric crypto needs replacing — which is why NIST standardized post-quantum algorithms like ML-KEM (Kyber) and ML-DSA (Dilithium), now being deployed in hybrid TLS.

Q
Why should you never use Hash(key ∥ message) as a MAC?
Model answer

Because hash functions built on the Merkle–Damgård construction, like SHA-256, are vulnerable to length-extension attacks. The digest of Hash(key ∥ message) is the function's internal state after processing that input, so an attacker who sees the digest — without knowing the key — can resume from that state and compute a valid digest for key ∥ message ∥ extra, forging a MAC for a message they partly control. HMAC defeats this with its nested structure, hashing an inner keyed hash again with the outer key, so the internal state is never directly exposed. So the rule is to use HMAC, or a natively-MAC primitive like Poly1305, rather than naive concatenation.

Q
What is HKDF and how does TLS 1.3 use it?
Model answer

HKDF is a key derivation function with two stages: extract, which takes input keying material that may not be uniformly random — like a Diffie-Hellman shared secret — and a salt, and condenses it into a uniformly random pseudorandom key; and expand, which stretches that key into as many independent output keys as you need, bound to context labels. TLS 1.3 uses HKDF throughout its key schedule: it feeds the ECDHE shared secret through extract and then derives all the distinct keys the connection needs — separate keys for handshake traffic, application traffic in each direction, exporters, and resumption — each with its own label so they're cryptographically separated. It replaced TLS 1.2's ad-hoc PRF with this clean, analyzed construction.

Q
Explain OCSP stapling and why it's better than plain OCSP.
Model answer

Certificate revocation checking via plain OCSP has the client contact the CA's responder to ask whether a certificate is still valid, which has two problems: it leaks the user's browsing to the CA, since the CA learns every site you visit, and it adds latency and a hard dependency on the responder being up — many clients "soft-fail" and skip the check if it's slow, undermining it. OCSP stapling moves the work to the server: the server periodically fetches a signed, timestamped OCSP response from the CA and staples it into the TLS handshake, so the client gets fresh revocation proof without ever contacting the CA. That fixes the privacy leak and the latency, and Must-Staple can require it so an attacker can't strip the staple. It's better because it delivers the same revocation assurance without the privacy and availability costs.

Q
What is a JA3/JA4 TLS fingerprint and how is it useful when traffic is encrypted?
Model answer

It's a hash of the client's ClientHello — the first handshake message, which is sent in the clear and lists the TLS version, cipher suites, extensions, and curves the client supports. Different software builds that list differently, so the fingerprint identifies the client tool without decrypting anything: Chrome, a Python script, and Cobalt Strike each produce a recognisable fingerprint. That's powerful because encryption hides the payload but not the handshake metadata, so a sensor can flag known-bad tooling or spot a connection whose TLS stack contradicts its User-Agent — a browser User-Agent over a Python TLS fingerprint is a classic malware and scraper tell. It's a Pyramid-of-Pain tooling signal, so treat it as a strong pivot, not a verdict.

Q
Why was JA4 created if JA3 already existed?
Model answer

JA3 MD5s an ordered list of ClientHello fields, but TLS 1.3 lets clients shuffle and pad extensions — GREASE values and the padding extension — so the same software emits many different JA3 hashes and attackers deliberately randomise it, which breaks matching. JA4, from FoxIO in 2023, fixes this by sorting the cipher and extension lists before hashing so reordering no longer changes the result, and it uses a structured, human-readable format instead of one opaque MD5 — you can read the TLS version, SNI presence, cipher and extension counts, and ALPN right off the front. It also generalises into the JA4+ family for other protocols, like JA4H for HTTP and JA4X for certificates. So JA4 is the more robust, less evadable, more readable successor.

Q
What security properties does TLS give you, and what doesn't it give you?
Model answer

TLS gives confidentiality through symmetric AEAD encryption, integrity through the AEAD tag or MAC, server authentication through certificates and a handshake signature, and anti-replay within a connection because every record's nonce includes an implicit sequence number. What it doesn't give is non-repudiation: both endpoints hold the same session keys, so either could have produced any record, and a capture can't prove to a third party what the server said. It also doesn't hide metadata like IPs, timing, sizes or — without ECH — the SNI hostname.

Q
Walk me through how a client validates a server's certificate chain.
Model answer

The client builds a path from the leaf through the intermediates the server sent — or ones it caches or fetches via AIA — up to a root in its trust store. For every link it checks the issuer name and key identifier match, the signature verifies with an allowed algorithm and key size, the certificate is within its validity dates, and issuers have CA:TRUE, keyCertSign and respect any path-length and name constraints. Then the leaf-specific checks: the hostname is in the SAN, the EKU includes serverAuth, it isn't revoked, and it carries enough valid CT timestamps. Finally the CertificateVerify signature in the handshake proves the server holds the matching private key right now.

Q
A site works in the browser but your API client fails with "unable to get local issuer certificate". Why?
Model answer

Almost always the server isn't sending its intermediate certificate. Browsers paper over that by caching intermediates from other sites or fetching them via the AIA URL in the leaf, but curl, OpenSSL, Java, Go and most mobile stacks only use what the server sends plus their root store, so they can't build a path. You confirm it with openssl s_client -showcerts and fix it on the server by serving the full chain — leaf first, then each intermediate — rather than by disabling verification in the client.

Q
What is forward secrecy, and how can session tickets undermine it?
Model answer

Forward secrecy means a later compromise of the server's long-term private key doesn't expose past sessions, because each session's secret came from ephemeral Diffie–Hellman keys that were discarded — the long-term key only signed the exchange. RSA key exchange lacked it: the pre-master secret was encrypted to the server's RSA key, so stealing that key decrypts every recording. Session tickets reintroduce the problem quietly: in TLS 1.2 the ticket contains the master secret encrypted under a server ticket key, so whoever steals the ticket key can decrypt every session resumed with it. The fix is rotating ticket keys every few hours and, in TLS 1.3, resuming with psk_dhe_ke so a fresh ECDHE exchange is mixed in.

Q
Why does TLS 1.3 still put "TLS 1.2" in its version fields?
Model answer

Because of middleboxes. During deployment testing, firewalls and inspection appliances broke a few percent of connections that showed an unfamiliar version number or message flow, so TLS 1.3 freezes the record and legacy_version fields at 0x0303, negotiates the real version in the supported_versions extension, sends a fake session ID and even a dummy ChangeCipherSpec so the exchange looks like a TLS 1.2 resumption. GREASE adds random reserved values to every list so implementations that choke on unknown values get caught early and the protocol stays extensible.

Q
How does TLS 1.3 protect against downgrade attacks?
Model answer

Three layers. Within 1.3, CertificateVerify signs a hash of the entire handshake transcript, and Finished MACs it, so altering the offered groups or suites breaks verification. Across versions, a 1.3-capable server that ends up negotiating 1.2 or lower writes the sentinel "DOWNGRD" plus a version byte into the last eight bytes of ServerHello.random, which is covered by the signature, so a 1.3 client that sees it knows an attacker stripped supported_versions and aborts. And there's simply less to downgrade to, because the weak suites and groups don't exist in 1.3.

Q
What is a HelloRetryRequest and when does it happen?
Model answer

In TLS 1.3 the client guesses a key-exchange group and sends a key share for it in the ClientHello. If the server doesn't accept any of the shares offered — for instance the client sent only the hybrid post-quantum X25519MLKEM768 and the server only supports P-256 — it replies with a HelloRetryRequest naming the group it wants, and the client sends a second ClientHello with the right share. It costs one extra round trip, and the server can include a cookie so it doesn't have to keep state between the two hellos, which helps against DoS.

Q
What is 0-RTT, and why is it risky?
Model answer

0-RTT lets a client resuming a session send application data in its very first flight, encrypted with a key derived from the PSK in the session ticket, so a returning visitor saves a whole round trip. The risk is replay: that early data has no fresh server input, so an attacker can capture it and resend it to the same or another server in the fleet, and it has no forward secrecy against the PSK. So servers either reject replays with single-use tickets or ClientHello caches, or — more realistically — only accept idempotent requests in 0-RTT and answer anything else with HTTP 425 Too Early.

Q
Explain the TLS 1.3 key schedule at a high level.
Model answer

It's a chain of three HKDF-Extract steps producing the Early Secret from the PSK, the Handshake Secret from the (EC)DHE shared secret, and the Master Secret. From each stage, HKDF-Expand-Label derives labelled secrets bound to a hash of the transcript so far: early traffic keys for 0-RTT, handshake traffic keys that encrypt the certificate and Finished, and application traffic keys plus exporter and resumption secrets. Traffic secrets are then expanded into an AEAD key and IV, and each record's nonce is the IV XORed with a sequence number. The design gives clean key separation between phases and directions, which is what made it formally analysable.

Q
How can you decrypt TLS traffic you've captured, and why doesn't the server's private key help with TLS 1.3?
Model answer

You either need the session secrets or you need to be in the middle. Clients and many servers can write a key log file when SSLKEYLOGFILE is set, and Wireshark uses it to decrypt TLS 1.2, 1.3 and QUIC. The old trick of loading the server's RSA private key only works for TLS 1.2 RSA key exchange, because there the pre-master secret was encrypted to that key; with ECDHE — and always in TLS 1.3 — the private key only signs, so it reveals nothing about session keys. For production visibility, organisations terminate and re-inspect at a proxy or read plaintext on the endpoint.

Q
What's the difference between CAA and Certificate Transparency?
Model answer

CAA is a preventive DNS record saying which CAs may issue for your domain, optionally down to a specific ACME account; honest CAs must check it before issuing, but a compromised or malicious CA can simply ignore it. Certificate Transparency is detective: every publicly trusted certificate must be logged in public append-only logs and carry signed timestamps proving it, so browsers reject unlogged certificates and domain owners can monitor for anything unexpected. You want both — CAA to stop accidental or social-engineered issuance, CT monitoring to catch the cases CAA can't stop.

Q
How do Merkle trees make CT logs trustworthy?
Model answer

The log stores certificates as leaves of a binary hash tree and signs the root hash as the Signed Tree Head, which commits to every entry. An inclusion proof shows a certificate is in the tree using only about log2(n) sibling hashes — around 30 for a billion entries — and a consistency proof shows an older tree is a prefix of a newer one, meaning the log only appended and never rewrote history. So a log can't secretly show different contents to different people or remove a mis-issued certificate without auditors noticing.

Q
Explain the TLS renegotiation attack, CVE-2009-3555.
Model answer

An attacker in the middle opened their own TLS session to the server and sent a partial HTTP request with no final newline, then forwarded the victim's ClientHello as a renegotiation inside that session. The victim completed a handshake directly with the server, but the server treated it as a continuation of the attacker's connection, so the victim's request — including their cookie — was appended to the attacker's prefix and executed with the victim's authority. The flaw was that the renegotiated handshake wasn't bound to the connection it occurred in; RFC 5746's renegotiation_info fixed that by including the previous Finished values, and TLS 1.3 removed renegotiation altogether.

Q
What's the difference between DV, OV and EV certificates, and why did browsers drop the EV indicator?
Model answer

DV certificates only prove control of the domain, via an HTTP file, DNS record or TLS-ALPN challenge; OV adds verification that the organisation exists; EV adds stricter identity vetting. Browsers check all three identically for the TLS connection — only the name binding is cryptographic. Chrome and Firefox removed the green EV bar in 2019 because research showed users didn't notice when it was missing, and researchers registered companies with colliding names to get misleading EV certificates, so the indicator added cost without meaningfully stopping phishing.

Q
How does mutual TLS differ between TLS 1.2 and 1.3?
Model answer

In both, the server sends a CertificateRequest and the client answers with its certificate and a CertificateVerify signature over the handshake to prove it holds the private key. In TLS 1.2 the client certificate travels in plaintext, so a passive observer learns the client's identity, and requesting a certificate mid-connection required renegotiation. In TLS 1.3 the CertificateRequest and client certificate are inside the encrypted flight, and a later request uses post-handshake authentication instead of renegotiation, although HTTP/2 forbids that, so modern designs authenticate the client up front.