Randhana.com
Published on
2 min read

How HTTPS Really Works: TLS Handshakes, Certificates and Encryption Explained

Authors
  • avatar
    Name
    Pulathisi Kariyawasam
    LinkedIn
    LinkedIn

Introduction

I started digging into this while setting up a TLS-secured database connection. What I thought was a simple checkbox turned into a rabbit hole TCP, certificates, certificate authorities, Diffie-Hellman, forward secrecy. Took me a while to piece together how it all connects.

So here's me trying to document that whole picture in one place, building from the ground up. By the end you'll see exactly how a client and server build trust, derive an encryption key without ever sending it over the wire, and keep all traffic unreadable even to someone capturing every packet.


Part 1: Network Foundations

Before TLS makes any sense, you need to know where it actually sits in the network stack.

The OSI Model Where TLS Lives

osi_model_layers
LayerNameWhat HappensExample Protocol
Layer 1-3Physical/NetworkData packets travel through internetIP, TCP
Layer 4TransportReliable connection between two devicesTCP
Layer 5-6Session/PresentationEncryption/Compression happens hereTLS/SSL
Layer 7ApplicationYour app receives decrypted dataHTTP, MySQL Protocol

TLS sits at layers 5-6. It wraps around whatever your app does at layer 7 and handles encryption before anything hits the wire.

TCP The Foundation Under TLS

TLS doesn't replace TCP. It runs on top of it. So before any TLS negotiation can happen, a TCP connection has to be established first. TCP uses a Three-Way Handshake:

three-way-handshake

1. Client sends: SYN

"Hello, I want to connect" with a sequence number (X) so the server can track ordering.

Seq=X, Flags=[SYN]

2. Server replies: SYN-ACK

"Got it, I'm here" server confirms and sends its own sequence number (Y).

Seq=Y, Ack=X+1, Flags=[SYN,ACK]

3. Client sends: ACK

"Confirmed" both sides now know the other is reachable.

Seq=X+1, Ack=Y+1, Flags=[ACK]

✓ TCP connection established. Reliable, ordered data transfer is now possible.

Dive Deeper: Run a packet capture with Wireshark you'll see these exact sequence numbers in real handshakes. Good way to make the theory concrete.

The critical point here: TCP is completely unencrypted. Every byte sent over a raw TCP connection is readable by anyone on the network between you and the server. That's exactly the problem TLS is there to solve.


Part 2: What Can Go Wrong Without Encryption

With only a TCP connection, the data flowing between client and server is naked on the wire. Three attacks become trivial:

  • Eavesdropping attacker on the same network reads your passwords, tokens, API keys in plaintext
  • Man-in-the-Middle (MITM) attacker sits between you and the server, silently reading and modifying messages in both directions
  • Impersonation a fake server convinces you it's the real one and you send it everything
👨 Client  →  🖥️ Server
⚠️ Anyone on the path can see and modify this data

The Solution: TLS

TLS (Transport Layer Security) and its older deprecated version SSL creates an encrypted tunnel on top of the TCP connection. Once that tunnel is up, even if someone captures every packet, they see noise.

Analogy: Instead of shouting across a crowded room (plain TCP), you and the server step into a soundproof booth. Anyone outside can see you're having a conversation but can't hear a word.


Part 3: Cryptography Basics

TLS uses two types of encryption, each solving a different problem. You need to understand both before the handshake makes sense.

What Encryption Does

Takes readable data (plaintext) and scrambles it into unreadable noise (ciphertext) using a mathematical key. Only someone with the right key can reverse it.

Plaintext: "password123"
Encrypt with key
Ciphertext: "x7#$@!kL9mP2q"
Decrypt with key
Plaintext: "password123"

Symmetric Encryption Same Key on Both Sides

Both sides use the same secret key to encrypt and decrypt.

  • ✓ Very fast simple bit operations (XOR, shifts)
  • ✓ Example: AES (Advanced Encryption Standard)
  • ✗ Problem: how do you safely get the shared key to the other side in the first place? If you send it over the network unencrypted, an attacker can steal it.

Asymmetric Encryption A Key Pair

Two mathematically linked keys: a public key (shared openly) and a private key (kept secret). What one encrypts, only the other can decrypt.

  • ✓ Solves the key-sharing problem public key can travel the network freely
  • ✓ Example: RSA, ECDSA
  • ✗ Much slower involves complex math like prime factorization and elliptic curves

How it works in practice:

Server's Public Key  = shared with everyone (like a public mailbox slot)
Server's Private Key = never leaves the server

Client:  DataEncrypt(data, Server's Public Key) → scrambled ciphertext
Network: attacker sees scrambled ciphertext  useless without private key
Server:  Decrypt(ciphertext, Server's Private Key) → original data ✓

Why Asymmetric Encryption is Too Slow for Real Traffic

Symmetric (AES):
├─ Simple XOR and bit shifts
├─ 1 MB → about 1 millisecond
└─ 10 MB → about 10 milliseconds ✓

Asymmetric (RSA-2048):
├─ Prime factorization, modular exponentiation
├─ 1 MB1000+ milliseconds (1 second!)
└─ 10 MB10+ seconds ✗

Key sizes tell the same story:

Same security level:
├─ Symmetric: 256-bit key
└─ Asymmetric: 2048-bit key needed (8× larger = much more computation)

This is why TLS uses both: asymmetric encryption once, to safely establish a shared secret then switches to symmetric for all actual data. The handshake (Part 6) is where this happens.


Part 4: Digital Certificates and Trust

Encryption solves the confidentiality problem. But it doesn't solve identity. Anyone can generate a key pair and claim to be your bank. Your data would be encrypted and perfectly readable by the attacker.

You"Hi bank.com, here's my password" (encrypted)
AttackerHas their own key pair, claims to be bank.com → decrypts your password ✗

This is the trust problem. Encryption without verified identity is useless.

Solution: Digital Certificates

A certificate is like a server's passport it ties a public key to a domain name, and it's signed by a trusted authority so you can verify it wasn't forged.

digital-certificate-trust

A certificate contains:

  1. Server's Public Key the actual encryption key
  2. Domain Name which domain this cert belongs to
  3. Validity Dates certificates expire
  4. CA's Digital Signature proof it was issued by a trusted authority
  5. Certificate Chain links back to a root that your browser already trusts

Certificate Authorities (CA)

A CA is a trusted organization like Let's Encrypt, DigiCert, or Google Trust Services that:

  • Verifies the domain owner is who they claim to be
  • Signs the certificate with their own private key
  • Has their root certificate pre-installed in your OS and browser

The trust works like this:

  1. Your browser has the CA's public key already installed
  2. Server sends a certificate signed with the CA's private key
  3. Your browser uses the CA's public key to verify that signature
  4. Valid signature → certificate is genuine → server is who it claims to be

Certificate Chain How Trust Flows

Certificates don't always link directly to a root CA. Often there's a chain of intermediate CAs. Here's a real-world example:

GlobalSign Root CA  ← pre-installed in your browser/OS (the trust anchor)
        ↓ signed
GTS Root R4
        ↓ signed
WE1  (Google Trust Services)
        ↓ signed
randhana.com's Certificate

The root CA at the top is what your browser already trusts. Everything below it is verified by walking up the chain. If the whole chain is valid, the server is trusted.

You'll often hear "SSL certificate" but the correct modern term is TLS certificate. SSL is deprecated. People still say SSL out of habit, it's technically wrong.


Part 5: How Browsers Actually Verify a Certificate

When the browser receives a certificate, it doesn't just read who signed it and take their word for it. It actively verifies the entire chain. Here's the step-by-step of what actually happens:

Step 1 Server sends the chain

Server sends: randhana.com cert → WE1 cert → GTS Root R4 cert

Step 2 Browser checks randhana.com's cert

  1. Is it expired? → No ✓
  2. Does the domain match what I'm connecting to? → Yes ✓
  3. Who signed it? → "Google Trust Services WE1" → verify signature using WE1's public key (from the next cert in chain)

Step 3 Browser checks WE1's cert

  1. Is it expired? → No ✓
  2. Who signed it? → "GTS Root R4" → verify signature using GTS Root R4's public key

Step 4 Browser checks GTS Root R4

  1. Is it self-signed? (issuer = subject, meaning it's a root) → Yes ✓
  2. Is this root in my pre-installed trust store? → Found ✓
  3. Does the self-signature verify? → Yes ✓

Result: Chain is TRUSTED ✓

One question that came up when I learned this: does the browser call the internet to verify? No. All root certificates are pre-installed on your device. The OS maintains and updates them. The browser may optionally check a revocation list (CRL or OCSP) to catch certs that were revoked early but the core chain verification is entirely local, no internet needed.


Part 6: The TLS Handshake Step by Step

Now all the building blocks are in place. TCP gives us a reliable connection. Cryptography gives us the tools. Certificates give us identity verification. The TLS handshake is where everything comes together.

This happens immediately after the TCP three-way handshake completes.

Steps

1. Client Hello

Client kicks things off:

TLS Version: 1.3
Supported Cipher Suites: TLS_AES_256_GCM_SHA384, etc.
Client Random: x7k#2q9p...  (random number, used later in key derivation)

2. Server Hello

Server picks the cipher suite both sides support:

TLS Version: 1.3 (agreed)
Cipher Suite: TLS_AES_256_GCM_SHA384
Server Random: m3y#9j5k...  (server's own random number)

3. Server Certificate

Server sends its certificate the signed document proving its identity. The client walks the chain exactly as described in Part 5.

4. Key Exchange Diffie-Hellman

This is the clever part. Both sides need to arrive at the same session key, but they can't just send it over the network that would defeat everything. Diffie-Hellman lets them each calculate the same key independently, from information that's safe to send publicly.

The paint color analogy makes this click:

Both sides know the same Public Base Color (not a secret, sent openly)

Client has:  Secret Color A  (never leaves client)
Server has:  Secret Color B  (never leaves server)

Step 1  Each mixes their secret into the public color and sends the result:
  Client sends: Mix(PublicColor + SecretA)ClientMix   (attacker can see this)
  Server sends: Mix(PublicColor + SecretB)ServerMix   (attacker can see this)

Step 2  Each takes what they RECEIVED from the other side, and mixes in their own secret:
  Client computes: Mix(ServerMix + SecretA)SessionKey
  Server computes: Mix(ClientMix + SecretB)SessionKey

Both arrive at the same SessionKey  without ever sending it.

Why the attacker can't crack it:
  Attacker sees: PublicColor, ClientMix, ServerMix
  Can't "un-mix" colors to recover SecretA or SecretB
  The math is one-way  easy to mix, impossible to reverse

Want to see the actual math behind this?
Diffie-Hellman Explained: How Two People Share Secrets Over Public Channels

✓ Both sides now have the same session key and neither of them sent it.

5. Handshake Verification

Both sides send a hash of the entire handshake conversation, encrypted with the new session key:

Client sends: Hash(all_handshake_messages) encrypted with SessionKey
Server sends: Hash(all_handshake_messages) encrypted with SessionKey

Hashes match → nothing was tampered with

6. Encrypted Channel Open ✓

From this point on, every byte is encrypted with the session key:

PlaintextAES_Encrypt(SessionKey)Ciphertext → sent on wire
CiphertextAES_Decrypt(SessionKey)Plaintext → received

Safe from eavesdropping, tampering, and impersonation.


Part 7: TLS 1.2 vs TLS 1.3 What Actually Changed

TLS 1.3 is a significant improvement over TLS 1.2. The biggest differences are speed and how the session key is established.

FeatureTLS 1.2TLS 1.3
Release Year20082018
Handshake Roundtrips2 roundtrips1 roundtrip
Key ExchangeBrowser generates key, encrypts it, sends itBoth sides calculate the same key (Diffie-Hellman)
Forward Secrecy⚠️ Only if ECDHE is configured (optional)✓ Always DH is mandatory in 1.3
0-RTT ReconnectionNoYes (faster reconnects for known servers)
Performance~100ms handshake~30-50ms handshake

TLS 1.2 Browser Creates and Sends the Key

Time 0ms:   ClientHello"I support these ciphers"

Time 10ms:  ServerHello + Certificate
"Use AES-256, here's my cert and public key"

Time 20ms:  Browser generates AES key → encrypts with server's public key → sends it
            ← the session key just traveled the network (encrypted, but still transmitted)

Time 30ms:  Server uses private key to decrypt → gets the session key

Problem: the browser unilaterally decided the key
         the key (even encrypted) traveled the network  extra round trip, extra risk

TLS 1.3 Both Sides Calculate the Key

Time 0ms:   ClientHello + Client's DH parameters
"I support these ciphers, here's my math input"

Time 10ms:  ServerHello + Server's DH parameters + Finished
"Using AES-256, here's my math input  I already computed the session key"

Time 20ms:  Browser computes session key from server's DH params
Sends Finished → connection open

The session key was never transmitted  only the DH math inputs were
Both sides contributed to it (bilateral)

Visual Comparison

TLS 1.2:
  ClientHelloServerHello + Cert
  Key Exchange (browser sends encrypted key)Finished
  Finished  [ ready  ~100ms ]

TLS 1.3:
  ClientHello + DH params →
ServerHello + DH params + Finished
  Finished  [ ready  ~30-50ms ]

One fewer roundtrip, no key transmitted, forward secrecy always on. TLS 1.3 is strictly better.


Part 8: Session Key What Happens After the Handshake

Once the handshake is done and both sides have the session key, the asymmetric encryption is set aside completely. Everything from here uses symmetric AES.

Why Switch to Symmetric?

Asymmetric solved the key distribution problem but using it for every packet would be way too slow:

Asymmetric (RSA)Symmetric (AES)
SpeedSlow complex mathFast simple bit ops
Role in TLSUsed once: establish the session keyUsed continuously: encrypt all data
Key size2048-4096 bits256 bits

After the Handshake, It Looks Like This

Client: "SELECT * FROM users"AES_Encrypt(SessionKey)"x7k#2q9p#@!$" → sent
Server: "x7k#2q9p#@!$"AES_Decrypt(SessionKey)"SELECT * FROM users" → executed

Session Key Properties Worth Knowing

  • Unique per connection new key derived for every TLS session, no reuse
  • Temporary deleted when the connection closes
  • Never transmitted both sides derived it independently via DH math
  • No need to go back to asymmetric once the session is established, the session key handles everything

Part 9: Forward Secrecy Can Someone Decrypt Old Traffic?

This question came up naturally while learning this: if someone captures all my HTTPS traffic today, and somehow gets hold of the server's private key later can they decrypt it?

The answer depends on how the session key was established.

Case 1 TLS 1.2 Without ECDHE (Decryptable)

In this setup, the browser generated the session key and sent it (encrypted with the server's public key). An attacker who later obtains the server's private key can:

  1. Decrypt the key exchange message to get the session key
  2. Use that to decrypt every packet in that session ✗

This is the core weakness of TLS 1.2 when ECDHE isn't used.

Case 2 TLS 1.2 / TLS 1.3 With ECDHE (Forward Secrecy)

The session key was derived from temporary Diffie-Hellman parameters generated just for that one handshake and discarded immediately after. Even with the server's private key in hand, there's nothing to decrypt the session key from. Those ephemeral secrets are gone forever.

This is forward secrecy: compromising the server's private key in the future doesn't expose any past sessions.

How to Check What Your Server Is Using

openssl s_client -connect yoursite.com:443

Look for this line in the output:

Server Temp Key: X25519, 253 bits

X25519 means ECDHE is in use forward secrecy is active. For TLS 1.2, look for ECDHE-RSA or ECDHE-ECDSA. If you see plain RSA without ECDHE no forward secrecy.

ConfigurationSecurityWhy
TLS 1.3 + X25519/ECDHE🟢 ExcellentModern, forward secrecy always on
TLS 1.2 + ECDHE🟡 GoodForward secure, older handshake
TLS 1.2 + RSA (no ECDHE)🟠 WeakNo forward secrecy
SSL 3.0 or TLS 1.0🔴 BrokenKnown attacks, don't use

Part 10: Keep-Alive Reusing the Connection

Setting up a TLS connection costs time: TCP handshake, then TLS handshake. That's real latency. Once a connection is open, you want to keep it alive and reuse it rather than paying that cost on every request.

Without Keep-Alive (Old HTTP/1.0)

Request 1: TCP open → TLS handshake → send → receive → connection CLOSED
Request 2: TCP open → TLS handshake → send → receive → connection CLOSED
Request 3: TCP open → TLS handshake → send → receive → connection CLOSED

3 requests × ~100ms handshake = 300ms wasted on setup alone

With Keep-Alive (HTTP/1.1+)

Request 1: TCP open → TLS handshake → send → receive → connection STAYS OPEN
Request 2: reuse connection → send → receive  (no handshake!)
Request 3: reuse connection → send → receive  (no handshake!)

Request 4 (35s later, after idle timeout): TCP open → TLS handshake → ...

Total for requests 1-3: ~120ms. Saved 180ms just by reusing the connection.

How the Keep-Alive Settings Work

The server controls this client can't override it.

Server response header:
Connection: keep-alive
Keep-Alive: timeout=30, max=100

timeout=30 → close connection after 30 seconds of no requests
max=100    → close after 100 requests, even if still active

In practice:

Request 1   → opens TCP + TLS          (count: 1/100)
Request 2   → reuses connection        (count: 2/100)
...
Request 100 → reuses connection        (count: 100/100  MAX REACHED)
              server closes connection

Request 101new TCP + TLS needed    (count: 1/100 again)

OR: if idle for 30+ seconds before hitting 100 → server closes → next request needs new connection

Configuring Keep-Alive on Your Server

Nginx

http {
    keepalive_timeout 30;
    keepalive_requests 100;
}

Apache

KeepAliveTimeout 30
MaxKeepAliveRequests 100

When Does a Connection Close?

1. Idle timeout expires  no requests for N seconds
2. Max request count reached
3. Client sends Connection: close header
4. Network error
5. Server restarts

Part 11: TCP vs UDP Why HTTPS Doesn't Use UDP

Everything covered so far uses TCP. But there's another protocol UDP and understanding why HTTPS doesn't use it makes clear what TCP is actually providing.

What is UDP?

UDP (User Datagram Protocol) is connectionless. No handshake. No acknowledgment. No guarantees. You fire a packet into the network and it either arrives or it doesn't.

TCP (like registered mail):
  Send → tracked → confirmed delivered → retried if lost

UDP (like dropping a note out a window):
  Send → it goes somewhere → might arrive, might not

TCP vs UDP

TCPUDP
ConnectionRequires handshakeNo connection
ReliabilityGuaranteed deliveryBest effort
OrderPackets arrive in orderNo guarantee
SpeedSlower (overhead)Faster
Lost packetsAuto-retriedGone forever
Keep-AliveNeededNot applicable
Use caseHTTPS, email, databasesVideo, games, DNS, VoIP

When Each Makes Sense

TCP when losing data is not okay:

  • HTTPS one missing byte can break a page
  • Email every message must arrive
  • Database queries data corruption is unacceptable
  • Banking every transaction must be confirmed

UDP when real-time matters more than perfect delivery:

  • Live video a dropped frame is better than freezing
  • Online games position updates are continuous, old data is worthless anyway
  • DNS a failed lookup just retries immediately
  • VoIP a brief silence is better than an awkward delay from retransmitting

Why HTTPS Has to Use TCP

  1. Completeness a web page must arrive in full. One missing byte in HTML, CSS, or JS breaks things.
  2. Order scrambled HTML is garbage.
  3. State sessions, cookies, login state need a persistent connection to track.
  4. Large data UDP has packet size limits (~64KB). TCP handles megabytes transparently.

UDP would break all of this. TCP is the only sensible choice for HTTPS.


Part 12: Putting It All Together MySQL Over TLS

Let's make everything concrete with a real scenario: an application connecting to a MySQL database over TLS.

mysql --host=db.example.com --user=root --password \
      --ssl-ca=ca-cert.pem \
      --ssl-cert=client-cert.pem \
      --ssl-key=client-key.pem

What Happens Under the Hood

Step 1 TCP Handshake (Layer 4) App and MySQL exchange SYN/SYN-ACK/ACK. TCP connection established.

Step 2 TLS Handshake (Layer 5)

  • MySQL sends its certificate
  • App verifies the cert chain against ca-cert.pem
  • Both derive a session key via Diffie-Hellman
  • Encrypted channel open

Step 3 MySQL Protocol (Layer 7) All queries and results flow encrypted:

App"SELECT * FROM users;"        (encrypted with SessionKey)
MySQL[{id:1, name:...}, ...]       (encrypted with SessionKey)

Step 4 Connection Closes Session key is discarded. Connection terminated.

The Certificate Files and What They Do

FileWhat It ContainsWho Uses ItPublic or Secret?
ca-cert.pemThe CA's certificateClient (to verify server)Public
server-cert.pemServer's public certificateServer (sends to client)Public
server-key.pemServer's private keyServer only🔒 Secret
client-cert.pemClient's cert (mutual TLS)Server (verifies client)Public
client-key.pemClient's private key (mutual TLS)Client only🔒 Secret

Part 13: Reading a Live TLS Connection OpenSSL

One of the most useful things to know is how to inspect what's actually happening in a TLS connection. The openssl CLI gives you everything:

openssl s_client -connect randhana.com:443 -servername randhana.com

This runs a full TLS handshake against the server and dumps all the details. Here's how to read it.

Certificate Details

FieldMeaningExample
s: (Subject)Who the certificate is forrandhana.com
i: (Issuer)Who signed itGoogle Trust Services (WE1)
Public Key TypeAlgorithm for asymmetric encryptionECDSA, 256-bit
NotBefore/AfterValidity windowMar 4 2026 → Jun 2 2026

Full Output Breakdown

Output LineWhat It Means
depth=2 ... GTS Root R4Root CA 2 hops from the server cert
depth=1 ... WE1Intermediate CA 1 hop from server cert
depth=0 ... randhana.comThe server's own certificate
verify return:1Each cert in the chain verified successfully ✓
PKEY: id-ecPublicKey, 256Server's public key: ECDSA, 256-bit
sigalg: ecdsa-with-SHA256Cert signed with ECDSA + SHA256
NotBefore: Mar 4, NotAfter: Jun 23-month validity window
TLSv1.3TLS 1.3 negotiated ✓
Cipher: TLS_AES_256_GCM_SHA384Symmetric cipher for data encryption
Server Temp Key: X25519, 253 bitsECDHE in use forward secrecy enabled ✓

Decoding the Cipher Suite

TLS_AES_256_GCM_SHA384
├─ TLS      = the protocol
├─ AES_256  = 256-bit AES symmetric encryption for data
├─ GCM      = Galois/Counter Mode  handles both encryption and integrity in one pass
└─ SHA384   = hash function for the integrity check (HMAC)

What the Key Exchange Line Tells You

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server Temp Key: X25519, 253 bits

TLS 1.3 is negotiated ✓
X25519 = ECDHE (ephemeral Diffie-Hellman) = forward secrecy on ✓
AES-256-GCM for all data ✓
SHA384 for integrity ✓

For TLS 1.2, look for ECDHE-RSA or ECDHE-ECDSA in the Temp Key line forward secrecy is on. If you see plain RSA with no ECDHE prefix it's not.


Part 14: Summary

The Full Lifecycle of One Secure Connection

1. TCP HandshakeReliable channel established (SYN / SYN-ACK / ACK)
2. TLS HandshakeIdentity verified + session key derived via Diffie-Hellman
3. Data TransferEverything encrypted with AES session key
4. Connection ClosesSession key discarded, gone forever

Key Concepts at a Glance

ConceptWhat It DoesHow
TCPReliable, ordered transport channelThree-way handshake
TLS HandshakeVerify identity + establish encryptionCertificates + Diffie-Hellman
CertificateBinds a public key to a domain nameSigned by a trusted CA
Private KeySigns certs, used in key exchangeNever leaves the server
Session KeyEncrypts all application dataDerived independently via DH
ECDHEEnsures past sessions stay privateEphemeral DH key discarded after use
Keep-AliveReuses connections to avoid repeated handshakesServer-configured timeout

Thanks for reading 👋