"The TLS handshake is the most important security ceremony on the Internet. Understanding it byte-by-byte is not an academic exercise -- it is the foundation for understanding every attack on encrypted traffic." -- Eric Rescorla, SSL and TLS: Designing and Building Secure Systems (Addison-Wesley, 2001), Preface
Lecture (100 min, two 50-min blocks)
6.1 Why TLS Matters for a Networking Curriculum
TLS sits at the intersection of networking and cryptography. From a networking perspective, TLS is a Layer-4/5 protocol that establishes encrypted sessions between TCP endpoints. It is the protocol underneath HTTPS, SMTPS, IMAPS, LDAPS, and most modern secure protocols.
A network engineer who does not understand TLS cannot:
- Debug why HTTPS connections fail or are slow (certificate issues, cipher negotiation mismatches)
- Understand what tools like mitmproxy or Burp Suite actually do
- Configure correct TLS termination at a load balancer or reverse proxy
- Understand what packet captures reveal about encrypted sessions
This week covers three TLS generations: TLS 1.2 (still widely deployed), TLS 1.3 (current standard), and QUIC-TLS (HTTP/3 transport).
6.2 TLS 1.2 Handshake
TLS 1.2 (RFC 5246) uses a two-round-trip handshake before the application data can flow. This is the protocol that modern networks are transitioning away from, but it remains dominant for legacy services.
Full TLS 1.2 handshake (RSA key exchange path):
Client Server
|---- ClientHello ---------------------->|
| (version, random, cipher suites) |
| |
|<--- ServerHello -----------------------|
| (selected cipher, server random) |
|<--- Certificate -----------------------|
| (X.509 cert with server's pub key) |
|<--- ServerHelloDone -------------------|
| |
|---- ClientKeyExchange ---------------->|
| (pre-master secret, encrypted with |
| server pub key -- RSA path) |
|---- ChangeCipherSpec ----------------->|
|---- Finished --------------------------> (encrypted)
| |
|<--- ChangeCipherSpec ------------------|
|<--- Finished -------------------------- (encrypted)
| |
|<--- Application Data --(2 RTT overhead)|
Master secret derivation (TLS 1.2 RSA path):
- Client generates 48-byte pre-master secret; encrypts with server's public key
- Server decrypts with private key; both sides now have pre-master secret
- Master secret = PRF(pre_master_secret, "master secret", client_random + server_random)
- Four session keys derived: client/server write keys, client/server MAC keys
The forward secrecy problem with RSA key exchange: the server's private key protects all pre-master secrets. If the private key is stolen (or subpoenaed, or compelled under national security letters) retroactively, all recorded TLS 1.2 RSA sessions can be decrypted.
TLS 1.2 with ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) key exchange solves this: the private key is ephemeral and discarded after the session. Stealing the server's certificate private key does not decrypt ECDHE sessions. TLS 1.3 mandates ECDHE; RSA key exchange is removed entirely.
6.3 TLS 1.3 Handshake
TLS 1.3 (RFC 8446, August 2018) achieves two goals over TLS 1.2: forward secrecy by default, and 1-RTT handshake (down from 2-RTT).
TLS 1.3 full handshake (1-RTT):
Client Server
|---- ClientHello ---------------------->|
| (version=TLS1.3, random, |
| key_share extensions with |
| client DH public key, |
| supported cipher suites) |
| |
|<--- ServerHello -----------------------|
| (server DH public key in key_share)|
|<--- EncryptedExtensions --------------- (encrypted from this point)
|<--- Certificate ----------------------- (encrypted)
|<--- CertificateVerify ----------------- (encrypted: server signs handshake)
|<--- Finished -------------------------- (encrypted: HMAC of handshake)
| |
|---- Finished --------------------------> (encrypted: client verifies)
| |
|<--- Application Data --(1 RTT overhead)|
Key derivation in TLS 1.3 (HKDF-based):
- (EC)DHE shared secret computed from client+server key_share values
- HKDF (HMAC-based Key Derivation Function) extracts and expands:
- handshake_secret -> handshake traffic keys (protect handshake records)
- master_secret -> application traffic keys (protect app data)
- resumption_secret -> session ticket (for 0-RTT)
Changes from TLS 1.2:
- Removed: RSA key exchange, static DH, RC4, DES, 3DES, MD5, SHA-1, compression, renegotiation
- Mandatory: (EC)DHE for all key exchanges (forward secrecy by default)
- Certificate is now encrypted (Server's identity hidden from passive observers)
- 0-RTT resumption: client can send application data in its first flight using a pre-shared key from a prior session (replay attack caveat; for non-sensitive idempotent requests only)
TLS 1.3 in Wireshark:
With a modern TLS 1.3 capture, you observe fewer record types. The server certificate is encrypted -- Wireshark shows encrypted handshake records after the ServerHello unless you provide the session keys (via SSLKEYLOGFILE).
# Set SSLKEYLOGFILE to capture TLS session keys from a browser
SSLKEYLOGFILE=/tmp/tls-keys.log firefox &
# In Wireshark: Edit -> Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename
# Now encrypted records are decrypted in the capture
6.4 Certificate Chains and PKI
A TLS certificate is an X.509 document binding a public key to a domain name, signed by a Certificate Authority.
Certificate chain (trust chain):
Root CA cert (self-signed; in OS/browser trust store)
|-- signs -->
Intermediate CA cert (intermediate; in server bundle)
|-- signs -->
Leaf/End-Entity cert (domain cert; presented by server)
The server presents the leaf cert and (usually) the intermediate cert. The client validates: leaf is signed by intermediate, intermediate is signed by a Root CA in the client's trust store.
Certificate fields:
- Subject: CN=www.example.com (or SAN: Subject Alternative Name, the modern approach)
- Issuer: CN=R3 (Let's Encrypt intermediate), O=Let's Encrypt
- Validity: notBefore + notAfter (certificates expire; this is a common outage cause)
- Public Key Info: key type (RSA 2048/4096, ECDSA P-256/P-384), public key value
- Extensions: basicConstraints (CA: true/false), keyUsage, extendedKeyUsage, subjectAltName, OCSP URL, CRL URL
Certificate validation failures (common causes of TLS errors):
- Expired certificate (most common; negligent renewal)
- Hostname mismatch (SAN does not include the hostname being connected to)
- Untrusted Root CA (self-signed cert not in trust store; common in internal infrastructure)
- Chain incomplete (server not sending intermediate cert)
- Revoked certificate (OCSP or CRL check fails; rare but serious)
OCSP Stapling: instead of the client querying the OCSP responder (which leaks browsing information and adds latency), the server periodically queries OCSP itself and staples the signed response to the TLS handshake. Preferred approach; verify it is enabled on production servers.
# Check OCSP stapling on a live server
openssl s_client -connect cloudflare.com:443 -status < /dev/null 2>&1 | grep -A 20 "OCSP Response"
6.5 QUIC-TLS (TLS 1.3 over QUIC)
QUIC (RFC 9000) is a new transport protocol built on UDP, developed by Google and standardized by the IETF in 2021. HTTP/3 uses QUIC as its transport.
QUIC integrates TLS 1.3 directly into its handshake -- there is no separate TCP handshake + TLS handshake. A QUIC connection establishes both transport and cryptographic security in the same 1-RTT exchange.
Why QUIC improves on TCP+TLS:
- Head-of-line blocking elimination: TCP delivers bytes in order; one lost packet blocks all subsequent data until retransmitted. QUIC multiplexes streams independently; a lost packet in stream 1 does not block stream 2.
- Connection migration: a QUIC connection ID is not tied to a (src_IP, src_port) tuple. When a phone switches from WiFi to cellular, the QUIC connection continues with a new IP address without reconnecting.
- 0-RTT resumption: returning clients can send application data in the first UDP datagram (replays are a risk; used for idempotent requests only).
- Encrypted header: QUIC encrypts most transport-layer metadata (sequence numbers, etc.) making network-level interference (middleboxes, firewalls) much harder.
# Capture HTTP/3 (QUIC) traffic
# QUIC uses UDP; default ports 443 and 80
tcpdump -i eth0 -n "udp port 443" -w /tmp/quic.pcap
# Check if a server supports HTTP/3 (Alt-Svc header)
curl -I https://cloudflare.com 2>&1 | grep -i alt-svc
# Test with HTTP/3 explicitly
curl --http3 https://cloudflare.com -v
6.6 mitmproxy: TLS Interception
mitmproxy is an interactive HTTPS proxy that intercepts, inspects, and modifies TLS-encrypted traffic. It works by:
- Intercepting the TCP connection from the client
- Establishing a separate TLS session with the server (acting as the client)
- Presenting a dynamically generated certificate to the client, signed by mitmproxy's own CA
- The client accepts only if mitmproxy's CA is trusted (usually installed manually for lab use)
This is exactly the attack a compromised enterprise proxy performs. It is also how corporate SSL inspection works in enterprises with BYOD policies.
# Start mitmproxy (transparent mode, all traffic on port 8080)
mitmproxy --listen-port 8080
# Configure browser proxy to 127.0.0.1:8080
# Install mitmproxy CA cert: ~/.mitmproxy/mitmproxy-ca-cert.pem
# For command-line interception:
mitmweb --listen-port 8080 # web UI at http://localhost:8081
Certificate pinning (seen in mobile apps) defeats mitmproxy: the app embeds the expected server certificate fingerprint or public key and refuses connections where the cert chain does not match. This is why corporate SSL inspection fails on pinned apps.
Lab Preview
Lab 4 dissects a TLS 1.3 handshake byte-by-byte:
- Capture a TLS 1.3 connection using
openssl s_client -connect cloudflare.com:443 -tls1_3 - Export session keys via SSLKEYLOGFILE; open capture in Wireshark with key material; decrypt the EncryptedExtensions and Certificate records
- Identify each TLS record type (ClientHello, ServerHello, EncryptedExtensions, Certificate, CertificateVerify, Finished)
- Correlate the key_share extension in ClientHello with RFC 8446 Appendix D (the transcript)
- Run mitmproxy against a test HTTPS server; observe the fabricated certificate in the browser; explain the certificate chain in the mitmproxy output
- Compare TLS 1.2 (captured via
openssl s_client -tls1_2) with TLS 1.3: count the RTTs; observe the visible vs. encrypted records
Homework
Reading (45 min): RFC 8446 (TLS 1.3), Section 1 (Introduction, 3 pages) and Section 4 (Handshake Protocol, focusing on §4.1 Key Exchange Messages and §4.4 Authentication Messages). For students with Rescorla: read Chapters 2-3 for background on the TLS design philosophy. Focus on understanding the key_share mechanism and why RSA key exchange was removed.
Hands-on (60 min): Run the following TLS comparisons and document your observations:
# TLS 1.3 session (inspect output carefully)
openssl s_client -connect cloudflare.com:443 -tls1_3 \
-msg -brief 2>&1 | head -60
# TLS 1.2 session (observe two-round-trip structure)
openssl s_client -connect cloudflare.com:443 -tls1_2 \
-msg -brief 2>&1 | head -60
# Certificate chain inspection
openssl s_client -connect cloudflare.com:443 -showcerts < /dev/null 2>&1 | \
grep -E "^(subject|issuer|notBefore|notAfter)"
# Check OCSP stapling
openssl s_client -connect cloudflare.com:443 -status < /dev/null 2>&1 | \
grep -A 5 "OCSP Response Status"
Document the cipher suite negotiated in each version, the certificate chain depth, and whether OCSP stapling was present.
Toolchain Diary Entry
First-introduce this week: openssl s_client / s_server; mitmproxy
openssl s_client -connect HOST:443: connect to a TLS server and print the full handshake and certificate chain. Fundamental diagnostic for TLS issues.
openssl s_client -tls1_3 -ciphersuites TLS_AES_256_GCM_SHA384: force specific TLS version and cipher suite for testing.
openssl s_server -cert cert.pem -key key.pem -port 4433 -tls1_3: spin up a simple TLS server for testing client behavior.
openssl x509 -in cert.pem -noout -text: decode and display all fields of an X.509 certificate.
openssl verify -CAfile chain.pem leaf.pem: manually verify a certificate chain.
mitmproxy --listen-port 8080: interactive intercepting proxy; intercepts HTTPS using a dynamically generated certificate signed by mitmproxy's CA.
mitmweb --listen-port 8080: same as mitmproxy but with a web-based UI at localhost:8081.
SSLKEYLOGFILE=/tmp/keys.log chromium &: capture TLS session keys from Chromium for Wireshark decryption.
Key Terms
- TLS 1.3 (RFC 8446): current TLS standard; 1-RTT handshake; mandatory (EC)DHE forward secrecy; removes RSA key exchange, RC4, 3DES; encrypts certificate in transit
- Forward secrecy: property where compromise of a long-term private key does not decrypt past sessions; achieved by using ephemeral (EC)DHE keys for each session
- X.509: ITU-T standard for public key certificate structure; used by TLS, S/MIME, code signing; contains Subject, Issuer, Validity, Public Key, Extensions
- Certificate chain: the sequence from leaf (end-entity) cert -> intermediate CA cert(s) -> root CA cert; client validates from leaf to trusted root
- OCSP: Online Certificate Status Protocol; allows clients to query whether a cert is revoked; OCSP Stapling has the server include a pre-fetched signed response in the handshake
- QUIC (RFC 9000): UDP-based transport protocol integrating TLS 1.3; eliminates head-of-line blocking; supports connection migration; foundation of HTTP/3
- mitmproxy: interactive TLS-terminating proxy; generates dynamic certificates for intercepted connections; used in security research, corporate SSL inspection, and penetration testing
- key_share: TLS 1.3 extension carrying the client's (EC)DHE public key; allows server to compute the shared secret and respond with encrypted records in a single round-trip
- 0-RTT resumption: TLS 1.3 feature allowing clients to send application data in the first flight using a pre-shared session ticket; vulnerable to replay attacks; suitable only for idempotent requests