In the previous lesson you solved trust: an X.509 certificate binds portal.medinube.example's public key to its identity with a CA's signature. But the certificate, sitting in a file, protects no bytes at all. What's missing is the protocol that puts it to use on every connection: TLS (Transport Layer Security), the layer underneath the "s" in HTTPS. This lesson has something special: you're barely going to learn any new primitives. You're going to see how ECDH with X25519 (04-04), certificates and their chain (05-01), signatures (04-03), HKDF (02-04), and AEAD (02-03) all click together, piece by piece, in the TLS 1.3 handshake — the course's "I already knew all of this" moment. Afterward we'll get our hands dirty: inspecting servers with openssl s_client, doing TLS right from Python (and fixing a legacy verify=False at MediNube), configuring nginx as a TLS terminator, and understanding mTLS and the contrast with SSH.
Contents
- What TLS guarantees and where it lives
- The TLS 1.3 handshake, piece by piece (you already know them all)
- Forward secrecy in TLS
- TLS 1.2 vs. 1.3 and the dead versions
- Practical inspection with
openssl s_client - Correct TLS from Python (and the sin of
verify=False) - The server side: nginx as a TLS terminator
- mTLS: when the client also identifies itself
- SSH, the other everyday protocol: TOFU vs. PKI
What TLS guarantees and where it lives
TLS is a protocol that turns an insecure channel (TCP) into one that delivers, in one shot, three of module 1's four goals:
- Confidentiality: nobody eavesdropping on the wire reads Ana Pérez's medical records.
- Integrity: nobody modifies data in transit without it being detected (the AEAD tag, 02-03).
- Server authenticity: Clínica Sol is talking to the real
portal.medinube.example, not to MalloryClinic (the certificate, 05-01). Client authenticity is optional (mTLS, below).
(Non-repudiation is not on the list: TLS uses MACs and shared symmetric keys for traffic, so either endpoint could have generated any given record. For non-repudiation, you need application-level signatures like the prescriptions from 04-03.)
Where does it live? Between transport and application:
| Layer | Protocol | Example |
|---|---|---|
| Application | HTTP, SMTP, IMAP... | GET /patients/ana.perez |
| Security | TLS | handshake + encrypted records |
| Transport | TCP | ports, retransmission |
| Network | IP | routing |
HTTPS isn't a different protocol from HTTP: it's HTTP running over TLS (port 443 instead of 80). The same holds for SMTPS, IMAPS, and so on. The application writes plaintext bytes and TLS encrypts them underneath; that's why adding TLS doesn't force you to rewrite the application. Important for MediNube: TLS protects data in transit; data at rest (the medical-records database) is a different problem, one we'll pick up again in 06-02.
The TLS 1.3 handshake, piece by piece (you already know them all)
Before encrypting the first application byte, client and server negotiate parameters, authenticate, and agree on keys. That's the handshake. In TLS 1.3 (RFC 8446, 2018) it costs a single round trip (1-RTT):
sequenceDiagram
participant C as Client (Ana Pérez's browser)
participant S as portal.medinube.example
Note over C: Generates an ephemeral X25519 pair (as in 04-04)
C->>S: ClientHello<br/>supported versions + ciphers +<br/>ephemeral X25519 public key + SNI
Note over S: Generates ITS OWN ephemeral X25519 pair<br/>ECDH → shared secret<br/>HKDF → session keys (02-04)
S->>C: ServerHello (ephemeral X25519 public key)<br/>🔒 Certificate (chain from 05-01)<br/>🔒 CertificateVerify (signature, 04-03)<br/>🔒 Finished
Note over C: ECDH + HKDF → the same keys<br/>Verifies the certificate chain (05-01)<br/>Verifies the CertificateVerify signature
C->>S: 🔒 Finished
Note over C,S: 🔒 Application data with AEAD:<br/>AES-256-GCM or ChaCha20-Poly1305 (02-03)
(🔒 = already encrypted with the freshly derived keys.) Let's walk through each piece and recognize where it comes from:
- ClientHello. The client proposes versions and cipher suites, and directly includes its ephemeral X25519 public key (in TLS 1.3 there's no waiting to negotiate: it bets on the most common curves and attaches the key share right away). It also sends the SNI (Server Name Indication): which domain it wants to connect to, essential when one IP serves multiple domains — it's the
-servernameyou used in 05-01. - Ephemeral ECDH (ECDHE). The server generates its own ephemeral pair, and both sides compute the shared secret. This is, literally, the X25519 exchange you coded in 04-04. The final "E" in ECDHE stands for ephemeral: use-once keys, a fresh pair per connection.
- Key derivation. Not a single byte of the raw ECDH secret gets used directly: it's run through HKDF (02-04) in a "key schedule" that derives different keys for each direction (client→server and server→client) and each phase. A rule you already knew: never use a raw DH secret, derive from it.
- Certificate. The server sends its leaf certificate plus the intermediates. The client validates the chain exactly as in 05-01: signatures up to a root in the trust store, dates, and the SNI's domain in the SAN. This is where MalloryClinic dies.
- CertificateVerify. A crucial subtlety: the certificate is public — Mallory could just replay MediNube's legitimate certificate. That's why the server additionally signs a transcript of the entire handshake with its private key (with ECDSA or RSA-PSS, the signatures from 04-03). Only whoever holds the private key matching the certificate can produce that signature, and since it covers the transcript (including the key shares), it can't be cut and pasted into another connection either.
- Finished (both sides). A MAC (module 3) over the transcript using the derived keys: confirms both sides saw the same handshake, unmodified.
- Application data. All traffic goes out with AEAD: AES-256-GCM or ChaCha20-Poly1305 (02-03), with nonces derived from a record counter — unrepeatable by construction, exactly as lesson 02-02 demanded.
Take inventory: X25519+ECDH (04-04), HKDF (02-04), certificates and chain (05-01), RSA-PSS/ECDSA signatures (04-03), MAC (03), AEAD (02-03), a CSPRNG for every ephemeral key (01). TLS 1.3 is your entire course, assembled by protocol engineers. There's no magic: there's careful composition of pieces you already master.
Forward secrecy in TLS
In 04-04 you saw that using ephemeral DH keys gives you forward secrecy: compromising long-term keys doesn't decrypt past traffic. In TLS 1.3 this isn't optional: every suite uses ECDHE. The private key behind portal.medinube.example's certificate is only ever used to sign (CertificateVerify), never to encrypt traffic or transport session keys.
Practical consequence the whole MediNube team should understand: if the certificate's private key leaks tomorrow (spoiler: in 05-03 we'll simulate exactly that incident), the attacker will be able to impersonate the portal until the certificate is revoked, but won't be able to decrypt a single byte of already-captured traffic. In old TLS 1.2 with RSA key-transport suites (no DHE/ECDHE), they could: record traffic today and decrypt it the day they got hold of the key. That's one of the big reasons behind the next table.
TLS 1.2 vs. 1.3 and the dead versions
| Aspect | TLS 1.2 (2008) | TLS 1.3 (2018) |
|---|---|---|
| Handshake round trips | 2-RTT | 1-RTT (faster; an optional 0-RTT exists, with replay caveats) |
| Forward secrecy | Optional (suite-dependent) | Mandatory (always ECDHE) |
| Supported ciphers | Dozens of suites, including bad ones: CBC with MAC-then-encrypt, RC4, 3DES, export-grade | Only 5 suites, all AEAD (AES-GCM, ChaCha20-Poly1305) |
| RSA as key transport | Allowed | Removed |
| How much of the handshake is encrypted | Almost nothing (certificate in the clear) | Everything from the ServerHello onward (the certificate travels encrypted) |
| Broken algorithms (MD5, SHA-1, weak DH groups) | Present in the ecosystem | Purged |
| Renegotiation and compression (sources of historical CVEs) | Present | Removed |
TLS 1.3 is, above all, an exercise in pruning: removing everything two decades of attacks (BEAST, POODLE, Lucky13, FREAK, Logjam...) proved fragile. Fewer options = fewer insecure combinations an administrator could pick by mistake. This is also crypto-agility (rule 8): the protocol negotiates version and suite, and that's exactly what made migration possible.
Dead versions — non-negotiable in 2026: SSLv2 and SSLv3 (POODLE, 2014), TLS 1.0 and TLS 1.1 (formally deprecated by RFC 8996 in 2021; browsers retired them in 2020). MediNube's policy, which we'll apply in nginx below: TLS 1.2 minimum, TLS 1.3 preferred. Well-configured TLS 1.2 (ECDHE+AEAD suites only) is still acceptable; anything older is not.
Practical inspection with openssl s_client
openssl s_client is the poor man's browser and the professional's diagnostic tool: it opens a TLS connection and tells you everything it negotiated.
# Basic connection; -servername sends the SNI (almost always necessary);
# -brief summarizes; without it you get the full dump.
openssl s_client -connect portal.medinube.example:443 \
-servername portal.medinube.example </dev/nullIn the (full) output, learn to spot these blocks:
CONNECTED(00000003) depth=2 C=US, O=..., CN=Trusted Root ← the chain, from the root (depth=2) depth=1 C=US, O=..., CN=Intermediate CA R3 ← to the intermediate (depth=1) depth=0 CN=portal.medinube.example ← and the leaf (depth=0) verify return:1 ← 1 = that link validates --- Certificate chain 0 s:CN=portal.medinube.example ← s: subject of each link i:C=US, O=..., CN=Intermediate CA R3 ← i: its issuer (must chain up!) 1 s:C=US, O=..., CN=Intermediate CA R3 i:C=US, O=..., CN=Trusted Root --- SSL handshake has read 4321 bytes ... New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 ← negotiated protocol and suite Server public key is 256 bit ← the certificate's key (P-256) Verify return code: 0 (ok) ← THE final verdict ---
The three golden lines: Verify return code: 0 (ok) (the chain validates against the trust store; any other code is a problem — 18 = self-signed, 10 = expired, 20 = missing intermediate...), New, TLSv1.3, Cipher is ... (which version and suite got negotiated), and the Certificate chain section (what the server sent; a production classic is forgetting to configure the intermediate, causing strict clients to fail).
Common diagnostic tricks:
# Does the server accept old versions? (it should FAIL):
openssl s_client -connect portal.medinube.example:443 -tls1_1 </dev/null
# Does it support TLS 1.3?
openssl s_client -connect portal.medinube.example:443 -tls1_3 </dev/null
# Test YOUR 05-01 lab, validating against YOUR root:
openssl s_client -connect localhost:8443 -servername portal.medinube.example \
-CAfile ~/medinube-lab/pki/ca.crt </dev/null
# See a remote certificate's dates at a glance (the 05-01 pattern):
openssl s_client -connect portal.medinube.example:443 \
-servername portal.medinube.example </dev/null 2>/dev/null \
| openssl x509 -noout -datesCorrect TLS from Python (and the sin of verify=False)
First day reviewing integrations at MediNube, and this gem turns up in legacy code that calls the external lab's API:
import requests
def call_api_BAD(lab_url: str, data: dict) -> dict:
# ⚠️ LEGACY CODE — DO NOT COPY ⚠️
# 2023 "fix": it was throwing SSLError: certificate verify failed,
# and this "fixed" it. (Narrator: it did not fix it.)
response = requests.post(
lab_url,
json=data,
verify=False, # ← THE CARDINAL SIN
timeout=10,
)
return response.json()What does verify=False do? It completely disables certificate validation: any certificate, from anyone, for any name, gets accepted. Traffic is still encrypted, and that's the psychological trap — "it's going out over HTTPS, what's the problem?" The problem is that it's encrypted toward who-knows-whom: MalloryClinic can present her homemade certificate from 05-01's exercise 3 and this code embraces it. It's, quite literally, undoing all of module 5 with one keyword argument. The original SSLError was the system working: the lab server was probably missing its intermediate, or it was an internal certificate whose CA needed to be distributed. The correct version:
import requests
def call_api(lab_url: str, data: dict) -> dict:
"""Calls the lab API with verified TLS.
verify=True is requests' default: it validates the chain,
dates, and name (SAN) against the CA bundle (certifi).
"""
response = requests.post(lab_url, json=data, timeout=10)
response.raise_for_status()
return response.json()
def call_internal_api(url: str, data: dict) -> dict:
"""Variant for internal services signed by MediNube's lab CA
(05-01): verify= can point at ONE specific CA.
That way we trust our internal root ONLY for this call,
without touching the system trust store.
"""
response = requests.post(
url, json=data, timeout=10,
verify="/etc/medinube/pki/ca.crt", # the lab root from 05-01
)
response.raise_for_status()
return response.json()Rules: never verify=False (not even "temporarily": 2023's temporary fixes are still in production in 2026); if the certificate comes from an internal CA, pass that CA's path in verify=; if validation fails against a third party, fix the root cause (tell the provider their chain is incomplete).
One level below requests, the stdlib:
import socket
import ssl
# create_default_context() is THE right way: it enables both certificate
# AND hostname validation, loads the system's CAs, and sets safe minimum
# versions. Don't build a bare SSLContext() by hand.
context = ssl.create_default_context()
# For internal lab services: add your root to the context.
# context.load_verify_locations("/etc/medinube/pki/ca.crt")
with socket.create_connection(("portal.medinube.example", 443)) as tcp:
# server_hostname does double duty: it sends the SNI and enables
# the name check against the SAN.
with context.wrap_socket(tcp, server_hostname="portal.medinube.example") as tls:
print("Protocol:", tls.version()) # e.g. 'TLSv1.3'
print("Suite:", tls.cipher()[0]) # e.g. 'TLS_AES_256_GCM_SHA384'
cert = tls.getpeercert() # dict with subject, SAN, dates
print("SAN:", cert.get("subjectAltName"))The equivalents of the cardinal sin at this level, which you'll find in more legacy code: ssl._create_unverified_context(), context.check_hostname = False, context.verify_mode = ssl.CERT_NONE. They're all verify=False in disguise; all of them must die in code review.
The server side: nginx as a TLS terminator
MediNube's Python application doesn't speak TLS directly: in front of it sits nginx as a TLS terminator (it receives HTTPS from the world, decrypts it, and passes plain HTTP to the application over the internal network). Advantages: TLS configuration lives in one place, maintained by people who think about nothing else, and the application stays simple. Essential configuration, annotated:
# /etc/nginx/sites-available/portal.medinube.example
# Block 1: port 80 ONLY redirects to HTTPS. No serving content
# in the clear "because it's just the landing page."
server {
listen 80;
server_name portal.medinube.example;
return 301 https://$host$request_uri;
}
# Block 2: the real service, TLS only.
server {
listen 443 ssl;
http2 on;
server_name portal.medinube.example;
# Certificate: fullchain = leaf + intermediates (remember the classic
# error you saw in s_client!). privkey = the private key, 600 permissions.
ssl_certificate /etc/medinube/tls/fullchain.pem;
ssl_certificate_key /etc/medinube/tls/privkey.pem;
# MediNube's version policy: 1.2 minimum, 1.3 preferred.
ssl_protocols TLSv1.2 TLSv1.3;
# For TLS 1.2, only ECDHE+AEAD suites (not needed for 1.3: all of them are).
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
# HSTS: tells the browser "for the next 2 years, talk to me ONLY over HTTPS."
# Closes the window of that first plain-HTTP visit. Brief mention: enable it
# once you're sure; with 'preload' it's nearly irreversible.
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://127.0.0.1:8000; # the Python app, on the internal network
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
}Don't memorize suites: use Mozilla's config generator (the "intermediate" profile) and testing tools (SSL Labs, testssl.sh) to verify the result. What matters is the what: 80→443 redirect, full chain, minimum versions, HSTS. How MediNube gets that fullchain.pem and how it renews itself, in the next lesson (05-03).
mTLS: when the client also identifies itself
So far, TLS authenticates the server; the client (Ana Pérez) authenticates afterward, inside the application, with her Argon2id password from module 3. But for machine-to-machine integrations — Clínica Sol's or Luna Medical Center's server calling MediNube's API — there's a better option: mTLS (mutual TLS), where the client also presents a certificate and signs its own CertificateVerify. The handshake is the same, with one extra step: the server requests a certificate (CertificateRequest) and the client responds with its own.
When should you use it? Service-to-service, with a small number of known parties: B2B integrations like the clinics, internal microservices, monitoring agents. When shouldn't you? For human end users (managing certificates in every patient's browser is unworkable; there, passwords+MFA or passkeys).
MediNube's design: clinic client certificates are issued by the lab's internal CA from 05-01, with SAN clinica-sol.clientes.medinube.example and centro-luna.clientes.medinube.example. In nginx, the server side is turned on like this:
# Inside the 443 server block of the integrations API:
ssl_client_certificate /etc/medinube/pki/ca.crt; # CA that issues clinic certs
ssl_verify_client on; # requires a client certificate
# The app receives the verified identity, with no API passwords to manage:
proxy_set_header X-Clinica-DN $ssl_client_s_dn;And the client, from Python: requests.post(url, cert=("/etc/clinica-sol/tls/client.crt", "/etc/clinica-sol/tls/client.key"), verify="/etc/clinica-sol/tls/medinube-ca.crt"). Notice the symmetry: each side validates the other against a CA; MalloryClinic can't impersonate either endpoint. The price is operational: issuing, renewing, and above all revoking clinic certificates when one closes its account — exactly the lifecycle covered in 05-03.
SSH, the other everyday protocol: TOFU vs. PKI
Close the mental map with the other secure protocol you use daily: SSH. Cryptographically it's a close cousin of TLS (key exchange with Curve25519, AEAD, Ed25519 signatures — your pieces again!), but it solves trust with a different model:
| TLS (Web PKI) | SSH (typical) | |
|---|---|---|
| Trust model | Trusted third parties (CAs) | TOFU: Trust On First Use |
| First connection | Verifiable chain from minute zero | Shows you the server key's fingerprint and asks ("Are you sure you want to continue connecting?") |
| Subsequent connections | The chain is revalidated every time | Compares against ~/.ssh/known_hosts; if the key changed, alarm |
| Weak point | The worst CA in the trust store | The first connection: if Mallory is in the middle that day, you accepted her key |
| Scale | Millions of unknown servers | A handful of servers you administer |
TOFU is reasonable when you connect infrequently to machines you know (and can verify the fingerprint through another channel); it doesn't scale to "any browser against any website." A loose thread worth leaving tied: SSH also supports certificates with its own CAs for large server fleets — once an organization grows big enough, it always ends up reinventing a PKI.
Common Mistakes and Tips
verify=Falseand its family (CERT_NONE,check_hostname=False,curl -k). Encrypting without authenticating is encrypting toward Mallory. Faced with a certificate error: diagnose withopenssl s_client, fix the root cause, never silence it.- Forgetting the intermediates on the server. Your browser might tolerate it (it caches intermediates), but
requestsor another backend will fail. Always serve thefullchain, and verify withs_clientthat theCertificate chainsection is complete. - Forgetting the SNI in tests. Without
-servername(or withoutserver_hostnamein Python), many servers return their default certificate and you'll think something's broken. Ifs_clientgives you a "weird" certificate, check the SNI before anything else. - Believing "HTTPS" makes the whole system secure. TLS protects transit. The database, the logs, the backups (06-02), and the application are still your problem — golden rule 9.
- Pinning exotic versions or suites by hand "for extra security." Creative configurations break clients and sometimes lower security. Mozilla's intermediate profile + automated testing.
- Terminating TLS at nginx and forgetting the internal network. A plaintext
proxy_pass http://is acceptable on localhost or a strictly controlled network; between hosts or different trust zones, use internal TLS (or mTLS) too. We'll pick this up in 06-02.
Exercises
Exercise 1 — Reading a handshake. Connect with openssl s_client (with -servername) to two public sites you use, and answer for each: (a) which version and cipher suite were negotiated? (b) how many links did the server send, and who's the intermediate? (c) what's the Verify return code? (d) does it accept TLS 1.1? Justify why the answer to (d) is what you'd expect in 2026.
Exercise 2 — Hunting the verify=False. This legacy MediNube code checks prescription status via Robles Pharmacy's API, which uses a certificate issued by the pharmacy consortium's shared internal CA (/etc/medinube/pki/farmacias-ca.crt). Identify every problem and rewrite it correctly:
import requests, urllib3
urllib3.disable_warnings()
def get_prescription_status_BAD(prescription_id):
r = requests.get(
"https://api.farmacia-robles.example/recetas/" + prescription_id,
verify=False)
return r.json()Exercise 3 — Your lab, served over TLS. Using the portal.crt/portal.key from 05-01's mini-PKI, spin up a local HTTPS server on port 8443 using http.server + stdlib ssl.SSLContext, and verify it with openssl s_client ... -CAfile ~/medinube-lab/pki/ca.crt, checking Verify return code: 0 (ok) and the negotiated version. (Hint: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ctx.load_cert_chain(...) + ctx.wrap_socket(httpd.socket, server_side=True); add 127.0.0.1 portal.medinube.example to /etc/hosts or use -servername.)
Solutions
Solution 1. Example session and reading:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep -E "New,|Verify" # → New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 (a) # → Verify return code: 0 (ok) (c) openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep -A6 "Certificate chain" # (b): count the s:/i: pairs openssl s_client -connect example.com:443 -tls1_1 </dev/null # → typically an error like "no protocols available" or a server alert (d)
(d) It must fail: TLS 1.0/1.1 are deprecated by RFC 8996 and retired from clients since 2020; also your own modern openssl binary may refuse to even try. A server accepting them today is an audit finding.
Solution 2. Problems: (1) verify=False: accepts any certificate — MalloryClinic could respond with fake prescriptions or capture identifiers; (2) urllib3.disable_warnings(): deliberately silences the warning that was flagging the problem — the "fix" that hides the fire; (3) no timeout: a hung API blocks the thread forever; (4) no HTTP error handling; (5) the underlying error was simply that the consortium's CA isn't public: the fix is to explicitly trust that CA, not all of them or none. Rewrite:
import requests
FARMACIAS_CA = "/etc/medinube/pki/farmacias-ca.crt"
def get_prescription_status(prescription_id: str) -> dict:
r = requests.get(
f"https://api.farmacia-robles.example/recetas/{prescription_id}",
verify=FARMACIAS_CA, # trust ONLY the consortium's CA
timeout=10,
)
r.raise_for_status()
return r.json()Solution 3.
import http.server
import ssl
httpd = http.server.HTTPServer(("0.0.0.0", 8443),
http.server.SimpleHTTPRequestHandler)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # server role, safe versions
ctx.load_cert_chain(certfile="/home/your_user/medinube-lab/pki/portal.crt",
keyfile="/home/your_user/medinube-lab/pki/portal.key")
httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
print("Serving at https://localhost:8443 ...")
httpd.serve_forever()Verification (the SNI/-servername makes the requested name match the certificate's SAN):
openssl s_client -connect localhost:8443 -servername portal.medinube.example \
-CAfile ~/medinube-lab/pki/ca.crt </dev/null 2>/dev/null | grep -E "New,|Verify"
# → New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
# → Verify return code: 0 (ok)Without -CAfile you'd get code 19/20 ("self-signed certificate in certificate chain" / "unable to get local issuer certificate"): your lab root isn't in the system trust store — and it should stay that way; it's trusted explicitly, call by call.
Conclusion
TLS was the pop quiz you'd already passed: ECDHE with X25519 to agree on secrets with forward secrecy (04-04), HKDF to derive session keys (02-04), the X.509 certificate and its chain to know who you're talking to (05-01), a CertificateVerify signature proving possession of the private key (04-03), and AEAD for all traffic (02-03) — packaged into a single-round-trip handshake. Along the way you learned the part that never makes it into the theory books: reading openssl s_client like a professional, banishing verify=False and its disguises from MediNube's code, configuring nginx as a decent TLS terminator (1.2 minimum, 80→443, HSTS), reserving mTLS for clinic↔MediNube integrations, and placing SSH's TOFU model against the PKI.
What's left is the last mile, the one that separates a weekend deployment from a serious service: certificates expire. Who issues portal.medinube.example's certificate, who renews it at 4 a.m., who finds out before it expires, and what do we do the day the private key turns up somewhere it shouldn't? All of that is operations, it has its own protocol (ACME), and even an incident runbook. See you in 05-03: Certificate Lifecycle Management, the lesson that closes out the module.
Applied Cryptography Course
Module 1: Cryptography Fundamentals
- What Cryptography Is and What It's For
- Encoding, Obfuscation, and Encryption
- Randomness and Entropy
- Kerckhoffs's Principle and the Golden Rules
Module 2: Symmetric Cryptography
- Symmetric Encryption: AES and ChaCha20
- Modes of Operation
- Authenticated Encryption (AEAD)
- Key Derivation (KDF)
Module 3: Hashes, MACs, and Passwords
Module 4: Asymmetric Cryptography
- Public-Key Fundamentals and RSA
- Elliptic Curve Cryptography
- Digital Signatures
- Key Exchange: Diffie-Hellman
- Hybrid Encryption
