SR

← Blog

The TLS journey

Networking · Part 4 of 4 · Interactive · ~20 min, noob to hero

Networking series · Part 4 of 4

You type https:// a hundred times a day. A tiny padlock appears and you move on. This tutorial opens the padlock. We'll follow one certificate from birth (a key pair on a server), through a Certificate Authority, onto the server, and then watch a frontend use it to talk to a backend safely.

The whole thing in a real-world story. A server wants to prove "I really am api.example.com". So it gets an ID card (a certificate) from an ID office (a Certificate Authority, or CA). The office checks the server really owns that domain, then stamps the card with a stamp nobody else can fake. Your browser is a guard who already knows what the trusted offices' stamps look like. When you visit, the server shows its ID card, the guard checks the stamp, and the two of you agree on a secret code word. From then on, everything is whispered in that code. That's it. The rest of this page is the details.

Words in plain English

You'll seeIt just means
Certificate (cert)A digital ID card for a website: its name, its public key, who vouched for it, and until when.
CA (Certificate Authority)A company that issues ID cards after checking you own the domain. Let's Encrypt is a free one.
Public key / private keyTwo linked keys. The public one is a padlock you can hand out freely. The private one is the only key that opens it, and you never share it.
SignatureA tamper-proof stamp made with a private key. Anyone can check it with the matching public key.
HandshakeThe short chat at the start of a connection where both sides agree on how to talk securely.
Trust storeThe list of CAs your computer or browser already believes. It ships with your OS.

New to this? This is the last part of a series. If a word below feels unfamiliar, the first three parts cover the basics: how a website loads, why plain HTTP is unsafe and encryption basics.

Level 1 · Noob
The problem TLS solves

Your browser and a server talk through many strangers: Wi‑Fi, your ISP, a dozen routers. Plain HTTP is a postcard: anyone handling it can read or rewrite it. TLS (Transport Layer Security) makes three promises:

🕶️ConfidentialityNobody on the wire can read the data.
🧾IntegrityNobody can change it without being detected.
🪪AuthenticityYou're really talking to that server, not an impostor.

Two tools deliver these promises. Learn them and the rest is just choreography.

Tool 1: the key pair (asymmetric crypto)

A key pair is two mathematically linked keys. The public key is shared with everyone. The private key never leaves its owner. Two things you can do with them:

OperationUseWho can do it
Sign with private keyProve "I made this"Anyone can verify with the public key; only the owner can sign.
Encrypt with public keySend a secret to the ownerAnyone can encrypt; only the owner can decrypt.

Signing is the star of this story: it's how a certificate becomes unforgeable. Asymmetric math is slow, though, so TLS uses it only for the introduction.

Tool 2: the session key (symmetric crypto)

One shared secret key that both sides use to lock and unlock data. It's super fast (AES / ChaCha20), but both parties must somehow already have the same key. That's the classic chicken-and-egg problem, and the handshake exists to solve it.

The whole of TLS in one sentence: use slow asymmetric crypto and a trusted certificate to prove who the server is and agree on a fresh secret, then use that secret with fast symmetric crypto for everything else.

Level 2 · Apprentice
The journey in 11 steps

Click through. Part A happens once, when you set up a server. Part B is the one-time trust that's baked into your device. Parts C and D happen on every connection.

Level 3 · Pro
Play with the three trickiest ideas

① Agreeing on a secret in public (Diffie‑Hellman)

Step 10 sounds like magic: two strangers shout numbers across a room and end up with the same secret. Try it with tiny numbers. Real TLS uses elliptic curves and 256‑bit numbers, but the idea is identical.

Public and agreed in advance: prime p = 23, base g = 5. Everything is "mod 23".

② Be the browser: should I trust this certificate?

Pick a server's situation and see which checks your browser runs and how it reacts.

③ What does a snooper see?

Type a message the frontend sends to the backend, then compare what someone on the Wi‑Fi captures.

HTTP (no TLS)

HTTPS (TLS 1.3, the same bytes after encryption)

The ciphertext above is a visual stand‑in, not real AES, but the real thing looks just as random, and changes completely if you alter a single character.

Level 4 · Hero
Do it yourself

Make your own CA and certificate locally

You can replay the entire story on your laptop. This is exactly what tools like mkcert do.

# 1. Be a Certificate Authority: make a root key + self-signed root cert
openssl genrsa -out ca.key 4096
openssl req -x509 -new -key ca.key -days 3650 -subj "/CN=My Toy Root CA" -out ca.crt

# 2. Be the server: make a private key + a CSR for api.local
openssl ecparam -genkey -name prime256v1 -noout -out server.key
openssl req -new -key server.key -subj "/CN=api.local" \
  -addext "subjectAltName=DNS:api.local" -out server.csr

# 3. Be the CA again: sign the CSR
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 90 -copy_extensions copy -out server.crt

# 4. Look inside
openssl x509 -in server.crt -noout -text | less

# 5. Serve it and talk to it (add "127.0.0.1 api.local" to /etc/hosts)
openssl s_server -accept 8443 -cert server.crt -key server.key -www &
curl --cacert ca.crt https://api.local:8443/     # works: you trust your CA
curl https://api.local:8443/                       # fails: nobody else does

The last two lines are the whole lesson: the same certificate is valid or invalid depending purely on whether the client's trust store contains the issuer.

Install it on a real server

# Let's Encrypt does steps 1-5 of the journey for you:
sudo certbot --nginx -d api.example.com

# What it ends up wiring into nginx:
server {
  listen 443 ssl http2;
  server_name api.example.com;
  ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;  # your cert + the middle CA cert
  ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;    # never share this
  ssl_protocols       TLSv1.2 TLSv1.3;
}

Common gotcha: serve the fullchain file (your cert plus the middle CA cert), not just your own cert. Browsers often have the middle cert already and hide the mistake; curl and mobile apps don't, and fail with "unable to get local issuer certificate".

Inspect a live handshake

openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null
curl -v https://example.com 2>&1 | grep -E "SSL|TLS|subject|issuer|expire"

Or open DevTools → Security (Chrome) to see the protocol, cipher and chain for the current page.

TLS 1.3 vs 1.2 and other hero details

Frontend → backend: where TLS actually lives

When your React app does fetch("https://api.example.com/users"), you write no TLS code. The browser's network stack performs steps 7–11 before the first byte of your request is sent; the server (or a reverse proxy / load balancer in front of it) terminates TLS and hands plain HTTP to your app code, usually over a private network.

Browserfetch()
Load balancer / nginxTLS terminates here
App serverplain HTTP, private net
HTTPS 🔒 over the public internet
HTTP inside your VPC (or re-encrypt with mTLS)

Related things you'll meet: mixed content (an HTTPS page can't call an http:// API), CORS (unrelated to TLS, it's about which origins may read responses), and HSTS (a header telling browsers "never use plain HTTP for this site again").

Debugging cheat‑sheet

ErrorUsual cause
CERT_DATE_INVALID / expiredRenewal failed; or the client's clock is wrong.
ERR_CERT_COMMON_NAME_INVALIDThe site name you typed isn't in the cert's list of allowed names (wrong cert served, or you used the IP address).
ERR_CERT_AUTHORITY_INVALIDSelf‑signed cert, a private CA your device doesn't know, or the server forgot to send the middle CA cert.
SSL_ERROR_NO_CYPHER_OVERLAPClient and server share no TLS version or cipher.
Works in Chrome, fails in curl / Java / AndroidThe server forgot to send the middle CA cert (Chrome quietly fills the gap; curl doesn't).

Check yourself

The map to keep

  1. The server makes a key pair; the private key stays put.
  2. A CA verifies the domain, then signs the public key + name into a certificate.
  3. Your device ships with a list of roots it trusts; trust flows down the chain.
  4. On connect, the server shows its cert and proves it owns the private key by signing the handshake.
  5. Both sides derive a fresh shared secret that never crossed the wire.
  6. Everything after that is fast symmetric encryption with tamper detection.

That's TLS. Everything else, from HTTP/3 to mTLS to service meshes, is a variation on this one idea.