The TLS Handshake
The handshake is the opening exchange where TLS does all the expensive work: the two sides agree on a protocol version and cipher, the server authenticates itself with its certificate, and both derive a shared symmetric session key — all before a single byte of application data flows. Once the handshake completes, the connection is cheap; until it does, nothing useful has moved.
The handshake's round trips are a real, measurable part of connection latency. On TLS 1.2 the setup costs two round trips on top of TCP's own; TLS 1.3 cut that to one by default, added a zero-round-trip resumption mode, and deleted a generation of insecure options outright. On a 100 ms transcontinental path, dropping one round trip is 100 ms a user feels on every fresh connection.
2 RTT · 200 ms
1 RTT · 100 ms
0 RTT · ~0 ms
The TLS 1.2 Handshake
A 1.2 handshake takes two round trips before application data. The client opens with a ClientHello listing its supported TLS versions, cipher suites, and a random nonce. The server replies with a ServerHello picking one cipher suite, its certificate, the key-exchange parameters, and a ServerHelloDone. The client verifies the certificate, completes the key exchange, and both sides switch to encrypted records and exchange Finished messages.
The weakness of 1.2 is everything it permitted. It allowed RSA key transport, where the client encrypts the session key to the server's public key — with no forward secrecy — and it carried a long tail of weak ciphers (RC4, CBC modes with padding-oracle bugs, export-grade suites) that a downgrade attack could coax both sides into using. Each option was a configuration mistake waiting to happen.
TLS 1.3 and the One-Round-Trip Handshake
TLS 1.3 redesigned the flow so the client guesses the key-exchange group and sends its key share in the ClientHello. The server responds with its own share, its certificate, and a Finished — and the client can send application data with its first reply. That is one round trip, half of 1.2's cost, with no negotiation penalty in the common case.
It is also safer because of what it removed. RSA key transport, static Diffie-Hellman, RC4, CBC-mode ciphers, compression, and renegotiation are all gone; the only key exchanges left are ephemeral, so forward secrecy is mandatory, not optional. The cipher-suite menu shrank from dozens of mostly-broken choices to five strong AEAD suites. TLS 1.3 is faster and safer at the same time, which is rare.
# inspect a live handshake: version, cipher, and cert chain openssl s_client -connect example.com:443 -tls1_3 </dev/null # New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 # Server certificate ... subject=CN=example.com # Verify return code: 0 (ok) <- chain validated to a trusted root
Key Exchange and Forward Secrecy
The session key is never sent on the wire. Instead both sides run an ephemeral Diffie-Hellman exchange (ECDHE over an elliptic curve like X25519): each generates a one-time key pair for this connection, swaps public values, and independently computes the same shared secret. An eavesdropper sees both public values and still cannot derive the secret, because that requires a private value neither side transmits.
Forward secrecy is the payoff of the word ephemeral. Because the key pair is discarded when the connection ends, an attacker who later steals the server's long-term private key cannot decrypt yesterday's recorded traffic — that traffic used a session key the stolen key never touched. The old RSA-key-transport mode lacked this: one stolen private key decrypted every past session captured under it, which is exactly the "harvest now, decrypt later" risk forward secrecy defeats.
Session Resumption and 0-RTT
Resuming a previous session skips the certificate and key-exchange work. TLS 1.3 hands the client a pre-shared-key ticket after the first connection; on reconnect the client presents it and the two sides are back to encrypted traffic in one round trip — or in 0-RTT, the client sends application data in the very first flight alongside the ticket, before any round trip completes.
0-RTT buys latency but reopens a door: that early data is replayable. An attacker who captures a 0-RTT request can resend it, and the server has no handshake context yet to reject the duplicate. Sending a 0-RTT POST /transfer twice could move money twice. The rule is firm — 0-RTT is for idempotent requests only (a GET, a cacheable read); anything that changes state must wait for the full one-round-trip handshake.
TLS 1.2 needs two round trips to set up, still permits RSA key transport with no forward secrecy, and carries a long menu of weak and legacy ciphers a downgrade attack can target. Keep it enabled only for clients that genuinely cannot do 1.3, and disable the broken suites explicitly.
TLS 1.3 sets up in one round trip (0-RTT on resumption), makes ephemeral key exchange and forward secrecy mandatory, and offers only five AEAD cipher suites. It is both faster and safer — the default for anything you control on both ends — with 0-RTT's replay caveat the one operational footgun.
- Leaving TLS 1.0 and 1.1 enabled. They allow ciphers with practical attacks (BEAST, POODLE) and are out of PCI compliance; a downgrade can force a modern client onto them unless you disable them at the server.
- Allowing RSA key-transport cipher suites. They provide no forward secrecy, so one future theft of the server private key decrypts every past session recorded under it — the harvest-now-decrypt-later scenario.
- Enabling 0-RTT for non-idempotent requests. Early data is replayable, so a captured 0-RTT POST can be resent and executed twice, with no handshake context to detect the duplicate.
- Ignoring handshake RTT in latency budgets. Each fresh TLS connection adds one to two round trips before the first byte; on a 100 ms path that is 100–200 ms users feel that connection pooling and 1.3 would remove.
- Letting server and client clocks drift. Certificate validity is time-checked, so a host whose clock is days off rejects a valid certificate as not-yet-valid or expired, breaking every handshake until NTP corrects it.
- Set TLS 1.2 as the floor and 1.3 as the preferred version, disabling 1.0 and 1.1 outright so no downgrade can land a client on a broken protocol.
- Restrict cipher suites to ephemeral AEAD only — ECDHE with AES-GCM or ChaCha20-Poly1305 — so every session has forward secrecy and no padding-oracle exposure.
- Reuse connections with keep-alive and HTTP/2 or HTTP/3 so the handshake cost is paid once and amortized across many requests instead of per request.
- Restrict 0-RTT to idempotent, safe methods, or disable it, so a replayed early-data request cannot duplicate a state-changing operation.
- Run NTP on every TLS endpoint and alert on clock skew over a few minutes, because validity-period checks turn a drifting clock into a total handshake outage.
Knowledge Check
What does forward secrecy from ephemeral Diffie-Hellman protect against that RSA key transport does not?
- Decryption of old recorded sessions after the server's long-term key is later stolen
- An attacker forging the server's certificate to impersonate it live during the current handshake
- Tampering with the application data of the current live connection
- A downgrade of the connection to an older, weaker TLS version
Why is enabling 0-RTT early data risky for a request that transfers money?
- The early data is replayable, so a captured request can be resent and run twice
- The early data is sent in plaintext before the session key exists
- It adds an extra network round trip that delays the transfer past its configured timeout
- The server is no longer authenticated, so the money could go anywhere
How does TLS 1.3 achieve a one-round-trip handshake where TLS 1.2 needs two?
- The client sends its key share in the ClientHello, finishing key agreement a round trip sooner
- It skips server authentication entirely to save the extra exchange
- It opens fewer underlying TCP connections, which is what removes one full network round trip from setup
- It sends the first application data unencrypted to avoid waiting on a key
You got correct