Week 4: Cryptography Fundamentals

The padlock icon in a browser's address bar represents a genuinely deep stack of math, and you don't need to implement any of it yourself — but you do need a working mental model of what each piece actually guarantees, because every later week leans on it: passwords are hashed (Week 5), traffic is encrypted (Week 2, formalized here), secrets are protected at rest (Week 10), and artifacts are signed to prove they weren't tampered with (Week 10, 16). This week builds that model from hashing up through a full TLS handshake.

Module 4 of 15 Week 4 of 16 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Explain what hashing, symmetric and asymmetric encryption each actually guarantee
  • Walk through a TLS handshake and explain what a certificate proves
  • Generate keys, a self-signed certificate, and a digital signature with OpenSSL

1. Hashing: One-Way Fingerprints

A hash function takes any input and produces a fixed-size output — deterministic (same input always produces the same output), and, for a cryptographic hash, effectively impossible to reverse. It doesn't encrypt anything; it fingerprints it.

hashing in practice
$ echo -n "hello" | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824  -

$ echo -n "hellp" | sha256sum   # one character different
9595877cef721530a41e88e50a1f95e2b34a1f5eb757c25d5c9fce7bd8d9f7f  -
# completely different output -- the "avalanche effect": a tiny input
# change produces a wildly different hash, with no visible relationship

Hashing is what makes password storage safe (Week 5): a server stores sha256(password), never the password itself, and checks a login by hashing the attempt and comparing. It's also how file integrity is verified — Week 3's AIDE compares hashes, not full file contents, and a downloaded file's published checksum lets you confirm it wasn't corrupted or tampered with in transit.

MD5 and SHA-1 are broken — stop using them for security

Both have known collisions: two different inputs that produce the same hash, which defeats the entire point of a fingerprint. They're still fine for non-security uses (a quick checksum to catch accidental corruption), but never for passwords, signatures, or anything where an attacker choosing a colliding input matters. Use SHA-256 or better for anything security-relevant, and never raw hashing for passwords specifically — Week 5 covers why a purpose-built password hash (bcrypt/Argon2) is different again.

2. Symmetric Encryption: One Key, Shared

Symmetric encryption uses the same key to both encrypt and decrypt. It's fast and well-suited to large amounts of data — which is exactly why it does the heavy lifting inside TLS once a connection is established (Section 4).

the symmetric model
plaintext + key --[encrypt]--> ciphertext
ciphertext + SAME key --[decrypt]--> plaintext

# AES-256 is the modern standard. Whoever has the key can both encrypt
# and decrypt -- which is exactly the problem it creates:

The catch is key distribution: both parties need the same key before they can communicate securely, but how do you safely hand someone a key over a channel you don't yet trust? This is precisely the problem asymmetric encryption solves — which is why real systems almost always use both together, not one or the other.

Encryption vs. encoding vs. hashing — a common confusion

Base64 is encoding, not encryption — it's trivially reversible with no key at all, and exists purely to represent binary data as text. Hashing (Section 1) is one-way. Encryption (this section and the next) is two-way, but only with the right key. Seeing "base64" and assuming something is "encrypted" is a genuinely common and dangerous mistake.

3. Asymmetric Encryption: Public/Private Key Pairs

Asymmetric encryption uses a mathematically linked pair of keys: a public key you share freely, and a private key you never share with anyone. What one key encrypts, only the other can decrypt.

the two directions, and what each is for
# Direction 1: confidentiality
# Encrypt with the RECIPIENT'S public key -> only their private key can decrypt
# -> "only you can read this"

# Direction 2: authenticity (digital signatures)
# Encrypt (sign) with YOUR OWN private key -> anyone with your public key can verify
# -> "this genuinely came from me, and hasn't been altered since I signed it"

This solves symmetric encryption's key-distribution problem directly: you can publish your public key anywhere, insecurely, and anyone can use it to send you something only you can read — no pre-shared secret required. The tradeoff is speed: asymmetric operations are computationally far more expensive than symmetric ones, which is why TLS (Section 4) uses asymmetric crypto only briefly, to set up a connection, then switches to a fast symmetric key for the actual data.

a familiar example: SSH key auth
# Your laptop holds a PRIVATE key. The server holds your PUBLIC key
# (in ~/.ssh/authorized_keys). Logging in proves you hold the private key
# without ever sending it anywhere -- the server issues a challenge only
# your private key can correctly respond to.

$ ssh-keygen -t ed25519 -C "you@example.com"
# Generates id_ed25519 (private -- never share) and id_ed25519.pub (public -- share freely)
A signature proves integrity AND authenticity, not confidentiality

Signing something with your private key doesn't hide its contents — anyone can still read it. It proves two specific things: it genuinely came from the holder of that private key, and it hasn't been altered since it was signed. That's exactly what Week 10 needs when verifying a software package genuinely came from its publisher, unmodified.

4. TLS Handshakes & the Chain of Trust (PKI)

TLS is where hashing, symmetric and asymmetric encryption all come together to make HTTPS work. A simplified handshake:

a TLS handshake, simplified
Client                                          Server
  |--- "Let's talk TLS, here's what I support" -->|
  |<-- Server's certificate (contains its public key) --|
  |    Client verifies: is this cert signed by a CA I trust?
  |--- Client helps establish a shared symmetric key ------->|
  |    (using asymmetric crypto briefly, right here)
  |<===== rest of the conversation: fast, symmetric encryption =====>|

The certificate is the crucial piece: it's the server's public key, plus its domain name, signed by a Certificate Authority (CA) — a third party your browser already trusts. This is PKI (Public Key Infrastructure): a chain of trust from a small set of root CAs, through intermediate CAs, down to the certificate your browser actually receives.

the chain of trust
Root CA (trusted, pre-installed in your OS/browser)
   |  signs
   v
Intermediate CA
   |  signs
   v
example.com's certificate  <- this is what the server actually presents

# Your browser walks this chain backward: is this cert signed by something
# whose signer is signed by... eventually reaching a root CA it already trusts?
# If the chain breaks anywhere, or the domain doesn't match, you get a warning.

A certificate proves the server genuinely controls the domain it claims to — it says nothing about whether the server itself is trustworthy or secure, only that you're actually talking to who you think you're talking to. That distinction matters: a phishing site can have a perfectly valid certificate for its own look-alike domain.

What a padlock icon actually promises

HTTPS guarantees confidentiality (nobody can read the traffic in transit) and that you're talking to the domain the certificate says you are. It says nothing about the content being safe, honest, or the site itself being legitimate — paypal-secure-login.example.net can be fully HTTPS and fully a phishing site. This distinction is exactly why phishing (Week 6, 14) still works despite HTTPS being everywhere.

5. Hands-on with OpenSSL: Keys, Certs & Signing

Everything above is abstract until you've generated the artifacts yourself. OpenSSL is the standard command-line toolkit for exactly this.

generate a key pair, and a self-signed certificate
# A private key (RSA, 4096-bit) -- never share this file
$ openssl genrsa -out private.key 4096

# A self-signed certificate for that key (no CA involved -- fine for local
# dev/testing, but a browser will rightly warn about it in production)
$ openssl req -new -x509 -key private.key -out certificate.crt -days 365 \
    -subj "/CN=localhost"

# Inspect what's actually in a certificate
$ openssl x509 -in certificate.crt -text -noout
sign a file, and verify the signature
# Sign a file with your private key
$ openssl dgst -sha256 -sign private.key -out message.sig message.txt

# Extract the public key from your private key, to share/verify with
$ openssl rsa -in private.key -pubout -out public.key

# Verify the signature using ONLY the public key -- proving the signer
# held the private key, without that key ever being involved here
$ openssl dgst -sha256 -verify public.key -signature message.sig message.txt
Verified OK

Tamper with message.txt by even one character after signing it, and that same verify command fails — a direct, hands-on demonstration of Section 3's integrity guarantee, not just a description of it.

Never generate real production keys this casually

These commands are for learning and local development. A real certificate for a production service comes from a real CA (or an automated one like Let's Encrypt), and real private keys need to live somewhere protected — a secrets manager or HSM, not a plaintext file sitting in a repo. Week 10 covers secrets management properly.

6. Hands-on Exercise

Hands-on

Build and break your own PKI chain, then sign and tamper with a file

Generate a real (if small) certificate chain from scratch, inspect a real site's certificate, and prove digital signatures actually detect tampering.

Part 1 — Hashing and signing:

  1. Create a short text file, compute its SHA-256 hash, change one character, and recompute — confirm the hashes are completely different (the avalanche effect from Section 1).
  2. Generate an RSA key pair, sign the original file, and verify the signature succeeds.
  3. Modify the file by one character (without re-signing) and confirm verification now fails — this is the exact mechanism Week 10 relies on to detect a tampered software package.

Part 2 — Build a mini certificate authority:

  1. Generate a "root CA" key pair and a self-signed root certificate (following Section 5's pattern, with -subj "/CN=My Root CA").
  2. Generate a second key pair for a "server" certificate, and a certificate signing request (CSR) for it using openssl req -new (not -x509 this time — a CSR, not a self-signed cert).
  3. Sign the server's CSR with your root CA's key using openssl x509 -req ... -CA ... -CAkey ..., producing a server certificate genuinely signed by your own mini CA rather than self-signed.
  4. Verify the chain: openssl verify -CAfile root.crt server.crt should report the server certificate as valid, because it can walk the signature back to a CA you told it to trust.
Hint

This mini exercise is exactly Section 4's chain of trust, just with a CA you created instead of a real one your browser already trusts — which is also precisely why your browser would still warn about it: trust in PKI comes from the root CA being one the verifier already trusts, not from the chain being technically valid.

Part 3 — Inspect a real certificate:

  1. Use openssl s_client -connect example.com:443 -showcerts to fetch a real site's certificate chain from the command line.
  2. Identify the certificate's subject, issuer, and validity dates from the output.
  3. Note how many certificates are in the chain (leaf, any intermediates) and write one sentence on what each one's signature is vouching for, working from Section 4's diagram.
Hint

Pipe the s_client output through openssl x509 -text -noout (after isolating one -----BEGIN CERTIFICATE----- block) for a readable breakdown, the same way Section 5 inspected your self-signed cert — the raw s_client output is noisy on its own.

7. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

Why is storing base64(password) instead of sha256(password) a serious security mistake?

Base64 is encoding, not encryption or hashing — it's trivially reversible with no key or computation required, so anyone with database access effectively has the plaintext password. A hash is one-way; even with the hashed value, recovering the original password isn't feasible (barring a weak/guessable password, which is what Week 5's password-hygiene section addresses separately).

Q2

Why does TLS use asymmetric encryption only briefly during the handshake, then switch to symmetric encryption for the rest of the connection?

Asymmetric encryption is computationally expensive compared to symmetric encryption. TLS uses asymmetric crypto for exactly the part that needs it — establishing a shared secret without a pre-existing shared key — then switches to fast symmetric encryption for the actual bulk data, getting the key-distribution benefit of asymmetric crypto and the speed of symmetric crypto.

Q3

A phishing site has a fully valid HTTPS certificate for its own domain. What does that certificate actually prove, and what does it not prove?

It proves the connection is encrypted, and that the server genuinely controls the domain named in the certificate. It proves nothing about whether that domain, or the site behind it, is legitimate or trustworthy — a look-alike domain can obtain a perfectly valid certificate for itself. HTTPS protects the channel, not the content or the destination's honesty.

Q4

You sign a file with your private key, then someone modifies the file by one byte. What happens when a verifier checks the signature against the modified file?

Verification fails. A signature is computed over the file's exact contents (via a hash, per Section 1's avalanche effect) — any change to the file, even one byte, produces a completely different hash, which no longer matches what was signed. This is exactly the integrity guarantee that lets Week 10's supply-chain security detect a tampered package before it's trusted.