Skip to content

TLS

TLS (Transport Layer Security) turns a readable, forgeable byte stream into a channel that is encrypted, tamper-evident and tied to a proven identity. It does this with one handshake that agrees on keys and checks a certificate.

TCP and IP deliver bytes in the clear. Anyone on the path (Wi-Fi, ISP, a compromised router) can read them, change them, or pretend to be the server.

  • SSL came from Netscape in the mid 1990s to protect HTTP. SSL 2.0 and SSL 3.0 both proved breakable. RFC 6176 (2011) prohibits SSL 2.0 and RFC 7568 (2015) deprecates SSL 3.0.
  • TLS 1.0 (RFC 2246, January 1999) was the IETF’s standardised successor. TLS 1.1 followed in April 2006 and TLS 1.2 (RFC 5246) in August 2008. TLS 1.2 added AEAD ciphers and SHA-256.
  • Years of attacks on old options (CBC modes, RC4, compression, static RSA, export ciphers, renegotiation) showed that the protocol carried too many choices.
  • TLS 1.3 (RFC 8446, August 2018) removed the dangerous options instead of patching them, encrypted most of the handshake, and cut it to one round trip. RFC 9846 (July 2026) is the current edition of TLS 1.3 and replaces RFC 8446.
  • RFC 8996 (March 2021) formally deprecated TLS 1.0 and 1.1: they depend on SHA-1 and lack AEAD ciphers.

What TLS gives you:

Property Meaning Mechanism in TLS 1.3
Confidentiality nobody on the path can read the data AEAD cipher (AES-GCM or ChaCha20-Poly1305) with keys from an ephemeral key exchange
Integrity nobody can change, drop or reorder data unnoticed the AEAD authentication tag on every record, and sequence numbers
Authentication you talk to the holder of the private key for that name X.509 certificate chain plus a signature over the handshake

What it does not give you: it hides neither the IP addresses nor the sizes and timing of traffic, and without ECH it does not hide the server name.

One round trip. It runs after the TCP handshake, or inside the QUIC handshake for HTTP/3.

  1. ClientHello (clear text). The client sends:
    • supported_versions: TLS 1.3 (the old version field stays at 1.2 so middleboxes do not choke);
    • cipher suites it accepts. TLS 1.3 has only five, all AEAD, for example TLS_AES_128_GCM_SHA256;
    • supported_groups and a key_share: an ephemeral public key for the group it guesses the server will pick, usually X25519 or P-256;
    • signature_algorithms;
    • server_name (SNI, Server Name Indication): the host it wants;
    • ALPN (Application-Layer Protocol Negotiation): h2, http/1.1;
    • a random value.
  2. ServerHello (clear text). The server picks the version, one cipher suite and one group, and sends its own ephemeral key_share. If the client guessed the wrong group, the server answers with HelloRetryRequest and the handshake costs one more round trip.
  3. Key agreement, ECDHE. Each side combines its private key with the other’s public key (Elliptic Curve Diffie-Hellman, ephemeral). Both arrive at the same shared secret. It never crosses the wire, and the ephemeral keys are thrown away afterwards.
  4. Key schedule, HKDF. HKDF (HMAC-based key derivation function) stretches secrets in stages: Early Secret (from a pre-shared key, or zeros), Handshake Secret (mixes in the ECDHE result), Master Secret. Each stage is bound to a hash of all handshake messages so far (the transcript). Separate keys come out for each direction and for handshake and application traffic.
  5. EncryptedExtensions. From here everything is encrypted with the handshake keys. This message carries the ALPN result and other extensions that used to be visible.
  6. Certificate. The server sends its leaf certificate and the intermediates. With mTLS it also sends a CertificateRequest first.
  7. CertificateVerify. The server signs the transcript hash with the private key of its certificate. This proves it holds the key, and binds the certificate to this exact handshake. RSA signatures must use RSA-PSS.
  8. Finished (server). An HMAC over the whole transcript, keyed from the handshake secret. Any tampering with earlier messages, such as stripping strong ciphers, makes it fail.
  9. Client verification. The client checks the chain up to a trusted root, the name against the SAN, the dates, revocation and CT data, and the CertificateVerify signature. Then it sends its own Finished, and its first request in the same flight.
  10. Application data. Both sides switch to application traffic keys. Each record holds at most 2^14 bytes of plaintext. Keys can be rotated mid-connection with KeyUpdate.
  11. NewSessionTicket. After the handshake the server sends one or more tickets for later resumption.
Client Server CLEAR ClientHello: versions, suites, key_share, SNI, ALPN ServerHello: chosen suite, key_share both sides derive handshake keys: ECDHE + HKDF HANDSHAKE KEYS {EncryptedExtensions}: ALPN result {Certificate}: leaf + intermediates {CertificateVerify}: signature over the transcript {Finished}: HMAC over the transcript client checks chain, name, dates, signature {Finished} APP KEYS [HTTP request] [HTTP response] [NewSessionTicket]: PSK for resumption
Only the two hello messages are readable on the wire. The server's four encrypted messages travel in one flight with ServerHello, so the client can send its Finished and its first request after a single round trip.

A certificate is an X.509 document that binds a public key to names, signed by a certificate authority (CA).

  • Root CA. Self-signed, lives in the trust store of the OS or browser, key kept offline.
  • Intermediate CA. Signed by the root. Does the daily signing, so the root key stays offline and an intermediate can be revoked.
  • Leaf. Your certificate. Signed by an intermediate.

The server must send the leaf and the intermediates. The client already has the root. A missing intermediate is the classic “works in my browser, fails in curl” bug, because browsers may cache or fetch intermediates and other clients do not.

What the client checks, in order:

  1. Each signature in the chain, up to a root in its trust store.
  2. Name. The requested host must match a subjectAltName (SAN) entry. A wildcard *.example.com covers one label only: a.example.com, not a.b.example.com and not example.com. The Common Name field is not used for this any more (RFC 9525).
  3. Validity. Now must be between notBefore and notAfter. A wrong client clock fails this.
  4. Key usage and extended key usage (serverAuth, or clientAuth for mTLS).
  5. Revocation and Certificate Transparency, depending on the client.

Validity periods are shrinking. The CA/Browser Forum Baseline Requirements (ballot SC-081) cap public TLS certificates at:

Issued on or after Maximum validity
before 15 March 2026 398 days
15 March 2026 200 days
15 March 2027 100 days
15 March 2029 47 days

Let’s Encrypt issues 90 day certificates by default, offers 6 day certificates, and plans a 64 day default in 2027 and 45 days in 2028. Manual renewal is no longer realistic. Automate it.

Certificate Transparency (CT). Every publicly trusted certificate is submitted to public append-only logs. The log returns a Signed Certificate Timestamp (SCT), which is embedded in the certificate. Chrome rejects certificates without enough SCTs: from at least 2 logs for certificates valid up to 180 days, 3 for longer ones. The effect for you: every certificate issued for your domains is public and searchable, so you can detect misissuance, and so can attackers enumerate your hostnames.

Revocation. See the table in Compare. In practice revocation is weak, which is why lifetimes are getting shorter. Let’s Encrypt removed OCSP URLs from its certificates in May 2025 and shut down its OCSP responders in August 2025. It publishes CRLs only.

ACME (Automatic Certificate Management Environment, RFC 8555, March 2019) automates issuance:

  1. The client creates an account key and submits an order for a list of names.
  2. The CA returns one authorization per name, each with challenges.
  3. The client proves control of the name by answering one challenge.
  4. The client sends a CSR (certificate signing request) to the order’s finalize URL.
  5. The CA issues, and the client downloads the certificate and chain.

Before issuing, the CA also checks the domain’s CAA record. See DNS.

Mutual TLS: the client presents a certificate too.

  1. The server adds CertificateRequest to its flight.
  2. The client answers with its own Certificate and CertificateVerify before Finished.
  3. The server validates that chain against the CA bundle it trusts for clients. This is usually a private CA, not the public roots.

The identity is whatever the certificate carries, usually a SAN: a DNS name, or a URI such as a SPIFFE ID in service meshes. mTLS authenticates the peer. Deciding what that peer may do is a separate authorization step.

Where you meet it: service to service traffic in a mesh, the Kubernetes API server and kubelets, database client certificates, partner APIs.

One IP address serves many names, so the server must know which certificate to present before anything is encrypted. SNI carries the host name in the ClientHello, in clear text. Anyone on the path can read it, and networks filter on it.

ECH (Encrypted Client Hello, RFC 9849, March 2026) closes that gap:

  1. The site publishes an ECH configuration with a public key in its HTTPS DNS record.
  2. The client builds the real ClientHello (the inner one, with the true SNI and ALPN) and encrypts it to that key with HPKE (Hybrid Public Key Encryption).
  3. It wraps the result in an outer ClientHello whose SNI is a shared public name of the provider.
  4. The client-facing server decrypts the inner hello and continues the handshake with it.

Observers see the provider’s public name and the IP address. ECH hides little unless many sites share that provider, and the DNS lookup must be encrypted too, or the name leaks there.

A full handshake costs a signature, a chain validation and a round trip. Resumption skips the certificate part.

  • TLS 1.2 had two mechanisms: session IDs (the server keeps state) and session tickets (the server encrypts the state and the client stores it).
  • TLS 1.3 has one: after the handshake the server sends a NewSessionTicket. The client later presents it as a pre-shared key (PSK). A ticket lives at most 7 days (604800 seconds). With the psk_dhe_ke mode a fresh ECDHE exchange still runs, so the resumed session keeps forward secrecy.
  • A resumed TLS 1.3 handshake is still one round trip, with no certificate and no signature.

0-RTT (early data). With a PSK the client can send application data in its very first flight, encrypted with a key derived from the PSK alone. Two costs:

  • No forward secrecy for the early data. It depends only on the ticket key.
  • Replay. The server has not contributed anything fresh, so an attacker who copies the first flight can send it again and the server may process it twice.

Rules that follow:

  • Send only requests that are safe to repeat, such as GET without side effects. Never a payment POST.
  • Servers defend with single-use tickets, a record of recent ClientHellos, or a freshness window. None of these is complete across a fleet of servers.
  • In HTTP, a proxy that forwards early data adds the header Early-Data: 1. An origin that does not want to risk it answers 425 Too Early, and the client retries after the handshake (RFC 8470).
SSL 3.0 TLS 1.0 TLS 1.1 TLS 1.2 TLS 1.3
Published 1996 January 1999 April 2006 August 2008 August 2018
Status deprecated, RFC 7568 (2015) deprecated, RFC 8996 (2021) deprecated, RFC 8996 (2021) in use, obsoleted as a specification by RFC 9846 current
Round trips, full handshake 2 2 2 2 1
Round trips, resumed 1 1 1 1 1, or 0 with early data
Key exchange static RSA, DH, DHE static RSA, DHE, ECDHE by extension static RSA, DHE, ECDHE by extension static RSA, DHE, ECDHE ECDHE or DHE only, always ephemeral
Forward secrecy optional optional optional optional, depends on the suite always
Record protection MAC-then-encrypt, RC4 or CBC RC4 or CBC RC4 or CBC CBC, or AEAD (GCM, ChaCha20) AEAD only
Handshake encrypted no no no no, the certificate is visible yes, after ServerHello
Cipher suites many many many many, each naming key exchange, signature, cipher and hash five, each naming only AEAD and hash

What TLS 1.3 removed: static RSA and static Diffie-Hellman key exchange, CBC and RC4 ciphers, compression, renegotiation, DSA, custom DHE groups, session IDs and the old ticket scheme, and ChangeCipherSpec (kept only as a dummy message for middlebox compatibility). It also added a downgrade signal: a TLS 1.3 server forced down to 1.2 writes a fixed marker into the last 8 bytes of its random value, and a TLS 1.3 client aborts when it sees it.

Method How it works Cost
CRL (certificate revocation list) the CA publishes a signed list of revoked serial numbers; clients or browser vendors download it lists grow large; freshness depends on download schedule
OCSP the client asks the CA’s responder about one certificate extra round trip, leaks which site you visit to the CA, clients usually ignore a failed lookup
OCSP stapling the server fetches the OCSP response and sends it in the handshake server must refresh it; only helps if the client insists on it
OCSP Must-Staple a certificate extension that makes a missing staple fatal brittle when the responder is down; Let’s Encrypt stopped issuing it in 2025
Short-lived certificates the certificate expires before revocation would matter needs fully automated renewal
Challenge Proof Port Wildcards Pick it when
http-01 serve a token at /.well-known/acme-challenge/ 80 only no one web server, port 80 reachable
dns-01 publish a TXT record at _acme-challenge.<name> none yes wildcards, hosts that are not public, many servers; needs DNS API credentials
tls-alpn-01 answer a TLS handshake with a special ALPN and certificate 443 no port 80 closed; mostly used by TLS-terminating proxies
Mode What the proxy does Proxy sees plaintext Back end sees Pick it when Cost
Termination (offload) ends TLS, forwards plain HTTP yes plain HTTP, client identity only in headers trusted private network, need L7 routing, WAF, caching traffic behind the proxy is readable
Re-encryption (bridging) ends TLS, opens a new TLS connection to the back end yes TLS from the proxy L7 features plus encryption in transit everywhere two handshakes, certificates on both hops
Passthrough forwards the TCP stream, may route on the clear text SNI no the client’s own TLS session end-to-end encryption, client certificates must reach the app no L7 routing, no header injection, no WAF
Mesh mTLS sidecar or node proxy on each side does mTLS between workloads only the local proxy plain traffic from its local proxy zero-trust between services without changing apps a private CA, rotation, more moving parts
Symptom Cause Check
certificate has expired renewal did not run, or the new file was not reloaded openssl s_client then -dates (below)
certificate is not yet valid client clock is wrong date -u
unable to get local issuer certificate, verify error 20 or 21 server does not send the intermediate, or the client lacks the root (private CA) openssl s_client -showcerts, count the certificates
hostname mismatch, no alternative certificate subject name matches the name is not in the SAN, or the proxy served its default certificate because SNI did not match -ext subjectAltName; compare with and without -servername
tlsv1 alert protocol version client and server share no TLS version openssl s_client -tls1_2, -tls1_3
handshake failure, no shared cipher no common cipher suite or signature algorithm openssl s_client -cipher ..., nmap --script ssl-enum-ciphers -p 443 host
wrong version number the port speaks plain text, or a proxy answered with HTTP curl -v http://host:443/
certificate required, bad certificate mTLS: the client sent no certificate, or one the server’s CA bundle does not trust curl --cert --key, check the CA list the server offers in s_client output
Works in a browser, fails in curl or Java missing intermediate, or a different trust store openssl s_client -showcerts, curl --cacert
Connection reset during ClientHello a middlebox filters on SNI try another SNI with --resolve, compare networks

Look at what the server presents:

Terminal window
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
Certificate chain
0 s:CN = example.com
i:C = US, O = Example CA, CN = Example Intermediate R1
1 s:C = US, O = Example CA, CN = Example Intermediate R1
i:C = US, O = Example CA, CN = Example Root
...
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
ALPN protocol: h2
Verify return code: 0 (ok)

s: is the subject and i: the issuer. Each i: must equal the next s:. One entry only means the intermediate is missing.

Terminal window
# dates, names and issuer of 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
# short summary: version, cipher, peer certificate, verification
openssl s_client -connect example.com:443 -servername example.com -brief </dev/null
# force a version
openssl s_client -connect example.com:443 -tls1_2 </dev/null
openssl s_client -connect example.com:443 -tls1_3 </dev/null
# is an OCSP response stapled
openssl s_client -connect example.com:443 -servername example.com -status </dev/null | grep -A3 'OCSP response'
# verify a chain from files
openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem
# does the private key match the certificate (compare the two hashes)
openssl x509 -in leaf.pem -noout -pubkey | openssl sha256
openssl pkey -in key.pem -pubout | openssl sha256

With curl:

Terminal window
curl -v https://example.com/ 2>&1 | grep -E 'SSL connection|ALPN|subject|issuer|expire'
curl --tlsv1.2 --tls-max 1.2 -sI https://example.com/ # TLS 1.2 only
curl --resolve example.com:443:203.0.113.7 https://example.com/ # test one back end with the right SNI
curl --cacert ca.pem --cert client.pem --key client.key https://api.internal.example/ # mTLS

TLS, SSL, TLS 1.3, TLS 1.2, RFC 8446, RFC 9846, handshake, ClientHello, ServerHello, HelloRetryRequest, key_share, supported_groups, X25519, P-256, ECDHE, Diffie-Hellman, forward secrecy, HKDF, key schedule, transcript hash, EncryptedExtensions, Certificate, CertificateVerify, Finished, AEAD, AES-GCM, ChaCha20-Poly1305, cipher suite, SNI, ALPN, ECH, HPKE, X.509, root CA, intermediate CA, leaf certificate, chain, trust store, SAN, wildcard certificate, validity, 47 days, Certificate Transparency, SCT, CRL, OCSP, OCSP stapling, Must-Staple, ACME, http-01, dns-01, tls-alpn-01, CAA, CSR, mTLS, client certificate, SPIFFE, session resumption, session ticket, PSK, NewSessionTicket, 0-RTT, early data, replay, 425 Too Early, TLS termination, passthrough, re-encryption, downgrade protection, openssl s_client.