Table of contents
- TLS Is a Secret Channel Built Over a Public Network
- Symmetric Keys — Fast, but Delivery Is the Problem
- Asymmetric Keys — Slow, but Safe Even If Published
- TLS’s Core Idea — Combining Two Types of Encryption
- Certificates — Can You Trust the Public Key?
- The Chain — Trusting Once Isn’t Enough
- The TLS Handshake — Step by Step
- Forward Secrecy — Protecting the Past
- Self-Signed Certificates — The Exception to the Trust Chain
- Let’s Encrypt and ACME — Automating Certificate Issuance
- TLS 1.3 — Faster and Simpler
- Summary — TLS Is Ultimately a Combination
TLS Is a Secret Channel Built Over a Public Network
At the end of Part 5, we said HTTPS is HTTP layered on top of TLS. So how does TLS (Transport Layer Security — a protocol that creates an encrypted channel between two hosts) create a secret conversation over a public network? You only need a few cryptographic fundamentals to answer this question.
TLS guarantees three things.
- Confidentiality: Third parties can’t see the content
- Integrity: Content isn’t altered in transit
- Authentication: The other party is verified to be who they claim to be
The TLS handshake ties all three together. In this post, we follow the handshake from its principles, unpacking why it had to be designed this way.
Symmetric Keys — Fast, but Delivery Is the Problem
The simplest way to encrypt a message is symmetric key encryption. The same key encrypts and decrypts. Modern symmetric ciphers like AES (Advanced Encryption Standard) achieve gigabit-level throughput with hardware acceleration. They’re fast.
flowchart LR
A[Alice] -->|Encrypt with same key K| M[Ciphertext]
M -->|Decrypt with same key K| B[Bob]
The problem is how to safely deliver that key to the other party. If you send the key in plaintext, the eavesdropper picks it up too. And you can’t exactly meet offline every time to exchange keys. This dilemma was a long-standing challenge in cryptography.
Asymmetric Keys — Slow, but Safe Even If Published
In the 1970s, asymmetric key encryption appeared. A public key and private key pair are generated. If you encrypt with the public key, only the private key can decrypt. RSA is the classic example, and today elliptic curve-based ECDSA and Ed25519 are also widely used.
flowchart LR
PUB["Public key<br/>(shared with everyone)"] -->|Encrypt| M[Ciphertext]
M -->|Decrypt| PRIV["Private key<br/>(only I know)"]
The public key can be freely published. Even knowing it, you can’t reverse-engineer the private key. So you scatter your public key across the internet and let anyone encrypt messages for you. The key delivery problem is solved.
But asymmetric encryption is computationally expensive. It’s too slow to use on large data wholesale. So TLS cherry-picks the strengths of both approaches.
TLS’s Core Idea — Combining Two Types of Encryption
TLS’s design philosophy is straightforward. Use asymmetric encryption to exchange a symmetric key, then use symmetric encryption for all subsequent data. Key exchange only happens once, so being slow is fine, and afterwards the fast symmetric cipher takes over.
flowchart TB
START[Handshake begins] --> ASYM["Asymmetric encryption<br/>(slow but safe key exchange)"]
ASYM --> SHARED["Shared session key established"]
SHARED --> SYM["Symmetric encryption for<br/>all application data"]
This transition point from “key exchange → symmetric encryption” is the heart of the handshake. Once the handshake completes, client and server share the same session key. From that point on, all HTTP requests and responses are encrypted with that session key.
Certificates — Can You Trust the Public Key?
Asymmetric encryption alone isn’t enough. When someone says “I’m naver.com” and offers their public key, how can you trust it? A mechanism to vouch that the public key truly belongs to naver.com is needed. That’s the certificate.
A certificate, in one sentence, is a guarantee that “the owner of this public key is this domain.” The entity that vouches is the Certificate Authority (CA). A certificate contains:
- Subject: Whose certificate is it (
CN=example.com) - Public Key: The subject’s public key
- Issuer: Which CA issued it
- Validity: Valid from when to when
- Signature: A value signed with the issuer’s (CA’s) private key
The “signature” is the key mechanism. The CA signs the certificate content with its private key. Anyone can verify that signature using the CA’s public key. If the signature checks out, you can be confident that “this certificate was truly issued by this CA.”
To inspect an actual certificate, use openssl.
openssl s_client -connect www.google.com:443 -showcerts < /dev/null 2>/dev/null \
| openssl x509 -noout -issuer -subject -dates
# issuer=C=US, O=Google Trust Services, CN=WR2
# subject=CN=www.google.com
# notBefore=Feb 12 08:33:40 2026 GMT
# notAfter=May 7 08:33:39 2026 GMT
The issuer is “Google Trust Services” and the subject is www.google.com. The validity period is also visible. The 90-day renewal cycle reflects the short-lived certificate policies of Let’s Encrypt and Google.
The Chain — Trusting Once Isn’t Enough
So who vouches for the CA’s public key? This is where the concept of a chain comes in. Browsers and operating systems ship with a pre-installed list of “trust these CAs” — the root CA list. Mozilla, Apple, Microsoft, and Google each maintain their own root store.
But root CAs are too important to issue certificates directly for every request. They delegate authority to intermediate CAs, and the intermediates issue actual server certificates. This relationship forms a chain.
flowchart TB
ROOT["Root CA<br/>(built into browser/OS)"] -->|signs| INT["Intermediate CA<br/>(e.g., Let's Encrypt R3)"]
INT -->|signs| LEAF["Server certificate<br/>(e.g., example.com)"]
When the browser receives a server certificate, it walks up the chain, verifying the signature at each step. If it reaches the root and matches an entry in the built-in root CA list, trust is established. If any step’s signature is broken or the root isn’t in the list, the famous “Your connection is not private” warning appears.
The strength of this structure is delegation. The root CA’s key is locked in a vault and used only to issue intermediate CAs. If an intermediate CA is compromised, only that intermediate is revoked — the root doesn’t need to be replaced.
The TLS Handshake — Step by Step
Now let’s walk through the complete handshake picture. This is based on TLS 1.2 (1.3 is covered separately later).
sequenceDiagram
autonumber
participant C as Client
participant S as Server
C->>S: ClientHello<br/>- Supported TLS versions<br/>- Supported cipher suites<br/>- Client random
S-->>C: ServerHello<br/>- Selected version and cipher suite<br/>- Server random
S-->>C: Certificate<br/>(server certificate chain)
S-->>C: ServerKeyExchange<br/>(DH/ECDHE parameters)
S-->>C: ServerHelloDone
Note over C: Verify certificate chain
C->>S: ClientKeyExchange<br/>(key exchange value)
Note over C,S: Both sides compute the same<br/>pre-master secret
Note over C,S: Random values + pre-master<br/>derive session key
C->>S: ChangeCipherSpec + Finished<br/>(encrypted from here)
S-->>C: ChangeCipherSpec + Finished
Note over C,S: All subsequent application data<br/>encrypted with symmetric session key
Let’s walk through what happens in the handshake.
- ClientHello: The client proposes “I support TLS 1.2/1.3, pick from these cipher suites, and here’s my random value.” A cipher suite is a bundle of “key exchange method + symmetric cipher + hash function”
- ServerHello: The server answers “Let’s go with this one. Here’s my random too”
- Certificate: The server sends its certificate (including the chain). The client verifies it up to the root CA
- ServerKeyExchange: Sends Diffie-Hellman public values for forward secrecy (explained below)
- ClientKeyExchange: The client sends its share of the public value
- Session key derivation: Both sides combine their independently calculated shared secret (pre-master secret) with the previously exchanged random values to produce identical session keys. This calculation happens independently on both sides but yields the same result
- Finished: Both sides encrypt a hash of all handshake messages so far with the session key and send it. If verified, it confirms the handshake wasn’t intercepted or tampered with
A single handshake costs 2 round trips (2 RTT). All subsequent data is encrypted symmetrically with the session key.
Forward Secrecy — Protecting the Past
The nearly mandatory feature in modern TLS handshakes is ECDHE (Elliptic Curve Diffie-Hellman Ephemeral — an ephemeral key created fresh for each connection). “Ephemeral” is the key word. A one-time key pair is generated and discarded for every connection.
The scenario this design protects against: Suppose an attacker records all of today’s encrypted traffic. Ten years later, they somehow steal the server’s private key. Can they decrypt the past traffic? No. The session keys were generated on the fly via ECDHE, and those one-time keys were already discarded. This is called forward secrecy.
The old RSA key exchange method meant that if the server’s private key was compromised, all past traffic could be decrypted. That’s why modern TLS configurations prioritize cipher suites that include ECDHE.
Self-Signed Certificates — The Exception to the Trust Chain
You can create a certificate without going through a CA. You sign it yourself — a self-signed certificate.
# Generate RSA private key
openssl genrsa -out server.key 2048
# Generate self-signed certificate (valid for 1 year)
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=localhost"
Browsers won’t trust this certificate (it’s not in the root CA list), so you’ll get a warning when connecting. Useful for development environments, but not for production. Even for internal certificates, you’d distribute an internal root CA to employees’ trust stores.
Let’s Encrypt and ACME — Automating Certificate Issuance
Until 2016, the biggest barrier to HTTPS was cost and hassle. You’d pay a commercial CA, manually create a CSR (Certificate Signing Request), go through email verification, download the file, and install it on the server. This had to be repeated annually.
Let’s Encrypt is a free CA that automated this entire flow. The underlying technology is the ACME protocol (Automatic Certificate Management Environment — RFC 8555). The entire process — from the client proving domain ownership to receiving a certificate — is defined as an API.
The most common ownership proof method is the HTTP-01 challenge.
sequenceDiagram
participant ACME as ACME Client (certbot)
participant CA as Let's Encrypt
participant WEB as Web Server<br/>example.com
ACME->>CA: Request certificate for example.com
CA-->>ACME: Place token X at /.well-known/acme-challenge/
ACME->>WEB: Deploy token at /.well-known/acme-challenge/X
CA->>WEB: Fetch http://example.com/.well-known/acme-challenge/X
WEB-->>CA: Return token X
CA->>ACME: Ownership confirmed, certificate issued
The CA sends an HTTP request directly to the domain. Only the person controlling that domain can serve the response, so ownership is indirectly proven. The DNS-01 challenge does the same thing via DNS TXT records. Wildcard certificates (*.example.com) require DNS-01.
Issuance takes a single command.
# certbot with automatic Nginx configuration
sudo certbot --nginx -d example.com -d www.example.com
Within this one command, key generation, CSR creation, challenge execution, certificate download, Nginx config modification, and automatic renewal cron registration all happen. In web infrastructure, cert-manager does the same job in a Kubernetes-native way, and modern proxies like Traefik and Caddy have it built in. This automation is the backdrop to HTTPS becoming the default.
TLS 1.3 — Faster and Simpler
TLS 1.3, standardized in 2018, takes a different philosophy from TLS 1.2. It boldly removes insecure options and simplifies the handshake.
Here’s what changed.
- Cipher suite cleanup: Vulnerable options like RSA key exchange, static DH, 3DES, MD5, and SHA-1 were all removed. As a result, ECDHE-based forward secrecy became mandatory
- 1 RTT handshake: The client sends its key exchange value alongside ClientHello. The server responds with ServerHello + certificate + Finished in one shot. The round trips are cut to one
- 0-RTT resumption: If you’ve connected to the server before, the first request can be sent alongside the handshake. There’s a replay attack risk, so it’s recommended only for safe requests (idempotent ones like GET)
- Encrypted handshake itself: In TLS 1.2, certificates were sent in plaintext. In 1.3, some handshake messages are also encrypted, improving privacy
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: TLS 1.3 1-RTT Handshake
C->>S: ClientHello + Key Share
S-->>C: ServerHello + Key Share<br/>+ Certificate (encrypted)<br/>+ Finished
C->>S: Finished
C->>S: Encrypted HTTP request<br/>(possible from second round trip)
HTTP/3 embedding TLS 1.3 directly inside QUIC is partly thanks to this simplicity. Faster new connection establishment means significantly better perceived performance.
Summary — TLS Is Ultimately a Combination
If you’ve followed along this far, you can see that TLS isn’t a single piece of magic but the result of combining multiple cryptographic techniques to fit each situation. Asymmetric encryption exchanges the key, symmetric encryption protects the data, digital signatures and certificate chains verify identity, and hashes with MACs ensure integrity. Each is a long-established technique, but the way they’re combined and the rigor of the protocol are what made TLS today’s standard.
And this security layer is no longer “optional.” It’s the baseline assumption for every web service. Let’s Encrypt and ACME removed the cost and effort, while TLS 1.3 and QUIC reduced the performance overhead. There’s hardly any reason left not to encrypt.
In the next post, we look at how this encrypted HTTP traffic is distributed in front of actual servers. L4 and L7 load balancers, reverse and forward proxies, algorithms from round-robin to ip-hash, how CDNs intercept traffic, and real-world products like nginx, HAProxy, Envoy, and Cloudflare — wrapping up the series.



Loading comments...