June 16, 2026
TLS 1.3 Handshake Explained Like You’re Debugging It in Production
Most TLS explanations either stay too high-level (“the client and server exchange keys and encrypt traffic”) or go so deep into RFC…
Sharmaamanrajesh
4 min read
Most TLS explanations either stay too high-level ("the client and server exchange keys and encrypt traffic") or go so deep into RFC terminology that engineers get lost in a sea of secrets, traffic secrets, transcripts, HKDF labels, and cryptographic jargon.
After spending hours understanding how TLS 1.3 actually works, I realized the easiest way to learn it is to follow the journey of a browser connecting to a website and focus on one question:
How do two machines generate the same encryption keys without ever exchanging them?
Let's break down the entire TLS 1.3 handshake step by step.
TLS 1.3 Handshake at a Glance
Before we dive into the individual steps, here's a high-level view of the entire TLS 1.3 handshake and key derivation process. Don't worry if some of the terms look unfamiliar — we'll revisit each component in detail throughout the article.
The diagram focuses on the conceptual flow of how a TCP connection evolves into an encrypted HTTPS session using ECDHE, HKDF, handshake keys, and application traffic keys.*
Step 1: TCP Handshake
Before TLS even starts, a TCP connection must be established.
Client Server
SYN -------------------->
<-------------------- SYN + ACK
ACK -------------------->Client Server
SYN -------------------->
<-------------------- SYN + ACK
ACK -------------------->At this point:
- No encryption exists.
- No certificates have been exchanged.
- No secrets exist.
We simply have a TCP connection.
Step 2: ClientHello
The browser now initiates the TLS handshake.
The ClientHello contains:
- Supported TLS versions
- Supported cipher suites
- Supported elliptic curves
- Random value
- SNI (Server Name Indication)
- Client ECDHE Public Key
Example:
{
"tls_version": "1.3",
"cipher_suites": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384"
],
"random": "abc123",
"server_name": "amazon.com",
"client_ecdhe_public_key": "aG"
}{
"tls_version": "1.3",
"cipher_suites": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384"
],
"random": "abc123",
"server_name": "amazon.com",
"client_ecdhe_public_key": "aG"
}The important part is the ECDHE public key.
The client secretly chooses:
aaand calculates:
aGaGwhere:
G = Generator PointG = Generator PointThe client sends:
aGaGto the server.
Notice:
The client never sends:
aaStep 3: ServerHello
The server receives the ClientHello.
The server secretly chooses:
bband calculates:
bGbGThe ServerHello contains:
- Selected TLS version
- Selected cipher suite
- Random value
- Server ECDHE Public Key
The server sends:
bGbGback to the client.
Again:
The server never sends:
bbStep 4: Certificate Exchange
The server now sends its certificate.
This certificate contains:
- Domain Name
- Public Key
- Issuer Information
- Signature from a Certificate Authority
Many engineers assume the browser contacts the Certificate Authority during every TLS handshake.
This is not true.
The browser already contains a local trust store.
Browser Trust Store
├── DigiCert Root CA
├── GlobalSign Root CA
├── Let's Encrypt Root CA
└── Sectigo Root CABrowser Trust Store
├── DigiCert Root CA
├── GlobalSign Root CA
├── Let's Encrypt Root CA
└── Sectigo Root CAThe browser verifies:
- Certificate chain
- Certificate signature
- Domain name
- Expiry date
All locally.
No call to the CA is typically required.
Step 5: ECDHE Shared Secret Generation
Now comes the magic.
The client knows:
aaand receives:
bGbGThe client calculates:
a × (bG) = abGa × (bG) = abGThe server knows:
bband receives:
aGaGThe server calculates:
b × (aG) = abGb × (aG) = abGBoth arrive at the same value.
Shared Secret = abGShared Secret = abGNeither side ever transmitted:
abGabGover the network.
This is the core of ECDHE.
Step 6: Handshake Secret
The shared secret is not used directly.
Instead, TLS feeds it into HKDF.
Shared Secret
│
▼
Handshake SecretShared Secret
│
▼
Handshake SecretThe Handshake Secret becomes the root for all handshake-related cryptographic material.
Step 7: Handshake Traffic Secrets
From the Handshake Secret, TLS derives:
Client Handshake Traffic Secret
Server Handshake Traffic SecretClient Handshake Traffic Secret
Server Handshake Traffic SecretThese are different values.
CHTS ≠ SHTSCHTS ≠ SHTSHowever, both the client and server independently derive these two secrets. Since both sides start with the same Handshake Secret and apply the same HKDF derivation process, they arrive at identical values for both the Client Handshake Traffic Secret (CHTS) and the Server Handshake Traffic Secret (SHTS).
Step 8: Handshake Keys
TLS derives:
Client Handshake Key
Server Handshake KeyClient Handshake Key
Server Handshake KeyThese keys are used only during the handshake phase.
Not for HTTPS traffic.
Not for application data.
Only for handshake protection.
Step 9: Finished Messages
The Finished message proves:
I derived the same secret as you.
The client sends:
FinishedFinishedencrypted using:
Client Handshake KeyClient Handshake KeyThe server verifies it.
Then the server sends:
FinishedFinishedencrypted using:
Server Handshake KeyServer Handshake KeyThe client verifies it.
At this point:
Both parties have proven possession of the same ECDHE-derived secret.
Step 10: Master Secret
Now TLS derives another secret:
Master SecretMaster SecretThink of this as the root secret for the rest of the connection.
Shared Secret
│
▼
Handshake Secret
│
▼
Master SecretShared Secret
│
▼
Handshake Secret
│
▼
Master SecretThe Master Secret is identical on both sides.
It is never transmitted.
It is never directly used to encrypt traffic.
Step 11: Application Traffic Secrets
From the Master Secret:
Master Secret
│
├────────────────────┐
│ │
▼ ▼
Client App Secret Server App SecretMaster Secret
│
├────────────────────┐
│ │
▼ ▼
Client App Secret Server App SecretThese are different values.
Client App Secret ≠ Server App SecretClient App Secret ≠ Server App SecretStep 12: Write Keys
From the Application Traffic Secrets:
Client Write Key
Server Write KeyClient Write Key
Server Write KeyThese are the keys that actually encrypt HTTPS traffic.
Step 13: HTTPS Traffic Begins
When the browser sends:
GET /productsGET /productsit is encrypted using:
Client Write KeyClient Write KeyThe server decrypts using the same Client Write Key.
When the server responds:
HTTP/1.1 200 OKHTTP/1.1 200 OKit encrypts using:
Server Write KeyServer Write KeyThe client decrypts using the same Server Write Key.
Complete TLS 1.3 Key Hierarchy
ECDHE
│
▼
Shared Secret
│
▼
Handshake Secret
│
├─────────────┐
│ │
▼ ▼
Client HS Server HS
Secret Secret
│ │
▼ ▼
Client HS Server HS
Key Key
│
▼
Finished Messages
│
▼
Master Secret
│
├─────────────┐
│ │
▼ ▼
Client App Server App
Secret Secret
│ │
▼ ▼
Client Write Server Write
Key Key
│
▼
Encrypted HTTPS TrafficECDHE
│
▼
Shared Secret
│
▼
Handshake Secret
│
├─────────────┐
│ │
▼ ▼
Client HS Server HS
Secret Secret
│ │
▼ ▼
Client HS Server HS
Key Key
│
▼
Finished Messages
│
▼
Master Secret
│
├─────────────┐
│ │
▼ ▼
Client App Server App
Secret Secret
│ │
▼ ▼
Client Write Server Write
Key Key
│
▼
Encrypted HTTPS TrafficIf you scroll back to Figure 1, you'll notice that the entire handshake can now be understood as a progression from TCP connection establishment to ECDHE key exchange, handshake verification, and finally application traffic encryption.
Final Thoughts
The biggest misconception about TLS is thinking that a single "session key" is generated and then used for everything.
TLS 1.3 is actually a hierarchy of secrets and keys.
The shared secret generated through ECDHE becomes the foundation from which handshake secrets, master secrets, traffic secrets, and finally encryption keys are derived.
Understanding this hierarchy makes concepts like:
- Forward Secrecy
- Key Updates
- Session Resumption
- TLS Record Encryption
much easier to understand.
Once you see TLS as a tree of secrets instead of a single key exchange, the entire protocol starts making sense.