On this page

TLS 1.2 Handshake

TLS 1.2 Handshake: ClientHello→ServerHello→Certificate→KeyExchange→Finished, completing key negotiation in two round trips. Understanding the cryptographic operations at each step (RSA vs ECDHE, signature vs key exchange) is a prerequisite for understanding why TLS 1.3 can reduce this to 1-RTT.

Overview

TLS (Transport Layer Security) is the foundation of secure internet communication, defined in RFC 5246 in 2008. It addresses three problems: authentication (who is the server), confidentiality (data is encrypted), and integrity (data is not tampered with). The TLS 1.2 handshake is the most widely deployed version—establishing a secure channel in 2-RTT, using ECDHE to ensure forward secrecy, and SNI allowing multiple domains to share a single IP. This article dissects the handshake process, key derivation, and session resumption mechanisms frame by frame.

Full Handshake (2-RTT)

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: ═══ RTT 1 ═══
    C->>S: ① ClientHello<br/>version + random + cipher_suites<br/>+ extensions (SNI, ALPN, supported_groups)

    S->>C: ② ServerHello + Certificate*<br/>+ ServerKeyExchange*<br/>+ CertificateRequest*<br/>+ ServerHelloDone

    Note over C: Verify certificate chain<br/>Compute pre_master_secret

    Note over C,S: ═══ RTT 2 ═══
    C->>S: ③ Certificate*<br/>ClientKeyExchange<br/>CertificateVerify*<br/>[ChangeCipherSpec]<br/>Finished

    Note over S: Decrypt pre_master_secret<br/>Derive session keys

    S->>C: ④ [ChangeCipherSpec]<br/>Finished

    Note over C,S: ✅ Secure channel established<br/>Encrypted application data transmission

    C->>S: Application Data (encrypted)
    S->>C: Application Data (encrypted)

* = optional (depends on cipher suite and server configuration)

ClientHello

struct {
    ProtocolVersion client_version;     // {3,3} = TLS 1.2
    Random random;                      // 32B: gmt_unix_time(4) + random_bytes(28)
    SessionID session_id;               // ≤32B (for resumption) or empty
    CipherSuite cipher_suites<2..2^16-2>;  // List of client-supported suites, prioritized
    CompressionMethod compression_methods; // {0} = null (null is mandatory in TLS 1.2)
    Extension extensions<0..2^16-1>;    // SNI, ALPN, supported_groups, signature_algorithms, ...
}

Key Extensions:

server_name (SNI): Domain name to access — server selects the correct certificate (multiple certs on one IP)
supported_groups: EC curves supported by the client (secp256r1, x25519, ...)
ec_point_formats: uncompressed (only value, practically useless)
signature_algorithms: Signature algorithms supported by the client (RSA-PKCS1-SHA256, ECDSA-SHA256, ...)
ALPN: Application layer protocol list ("h2", "http/1.1") — server selects one
Extended Master Secret: Prevents Triple Handshake attacks
SessionTicket TLS: Supports session tickets (stateless resumption)

Cipher Suite Format

Cipher Suite Breakdown: Suite Name = Four Cryptographic Parameters TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ECDHE Key Exchange Algorithm Elliptic Curve Diffie-Hellman Ephemeral RSA Server Authentication Public key type in certificate — RSA signing AES_128_GCM Symmetric Encryption AES-128-GCM AEAD SHA256 PRF Pseudo-Random Function, key derivation TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 ECDHE Key Exchange ECDSA Server Authentication EC Certificate CHACHA20_POLY1305 Symmetric Encryption ChaCha20-Poly1305 AEAD SHA256 PRF The four parts of the suite name can be combined independently: Key exchange algorithm determines forward secrecy, authentication algorithm determines certificate type, symmetric encryption determines data encryption method, PRF (fixed to SHA256 in TLS 1.2) is responsible for key derivation.

Key Derivation

1. From DH key exchange: pre_master_secret (computed jointly by server/client)
2. master_secret = PRF(pre_master_secret, "master secret",
                        ClientHello.random + ServerHello.random)[0..47]

3. From master_secret: 6 session keys
   key_block = PRF(master_secret, "key expansion",
                    ServerHello.random + ClientHello.random)
   → client_write_MAC_key, server_write_MAC_key,
     client_write_key, server_write_key,
     client_write_IV, server_write_IV

4. Finished message = PRF(master_secret, "client finished",
                           hash(all handshake messages))[0..11]
   → Ensures handshake was not tampered with

Session Resumption

Session ID (Stateful, RFC 5246):
  1. Initial full handshake → ServerHello.session_id = <random ID>
  2. Server caches: session_id → master_secret + cipher_suite
  3. Reconnection: ClientHello.session_id = <cached ID>
     → Server finds session → Skips Certificate + ServerKeyExchange
     → 1-RTT

Session Ticket (Stateless, RFC 5077):
  1. Server: Encrypts (master_secret + cipher_suite + ticket_lifetime)
     (using server's ticket key) → Sends to client → SessionTicket extension
  2. Reconnection: ClientHello.session_ticket = <encrypted blob>
     → Server decrypts ticket → Restores session → 1-RTT
     → Server does not need to store session state (cipher from ticket)

  Security: Ticket key must be kept secret + rotated regularly (ticket leak → historical session leak)

References

  • RFC: 5246, 6066, 7301, 5077, 7627 (Extended Master Secret)
  • Tools: openssl s_client -connect host:443 -tls1_2, Wireshark ssl.handshake

Keywords: TLS 1.2, ECDHE, cipher suite, session resumption, session ticket, SNI, ALPN, PRF