Cryptography › Module 1 › Lesson 2
MD5, SHA-1, SHA-256
Compare legacy MD5/SHA-1 with SHA-256—and when each is still seen in the wild
Opening
Old hashes still haunt systems — collision history is a defender warning, not a homework assignment to forge certs.
Last lesson, a hash was one-way, deterministic, avalanche, fixed length, and not encryption. “A hash” is still too vague for a design review. MD5, SHA-1, and SHA-256 are different families with different digest sizes and different public break stories. You will still meet MD5 in legacy software and checksum fields. You will still see SHA-1 in old git jokes and retired certificate lore. Knowing what is deprecated — and why — stops you from building new integrity or signature checks on sand. This is literacy at defender level: do not use broken collision resistance for signatures, certificates, or untrusted-download integrity. This is not a lab in producing MD5 collisions or forging a CA cert. SHA-256 (SHA-2 family, 256-bit digest) is the default you already used in Topic 7 and Lesson 1 for file integrity. Password storage is a different family — bcrypt, Argon2, scrypt — because those algorithms are slow on purpose. Do not “upgrade” a login database by wrapping passwords in SHA-256 and calling it modern. Next lesson is that split. Today you compare three general-purpose digests on a file you own, including openssl dgst, and you write which one you would publish next to an ISO. Still YOUR file. Still $HOME/cyberlium-lab. Still chmod 600. No dumps, no hashcat, no rainbow tables.
1. MD5 and SHA-1: collision history, and what “broken” means for a defender
MD5 produces a 128-bit digest (32 hex characters). It was designed in the early 1990s as a general cryptographic hash. Collision attacks against MD5 are practical: researchers (and later real incidents) showed two different inputs with the same MD5. For a defender, that kills MD5 as a proof that “this file is the file I meant” against a motivated attacker who can craft a colliding pair — especially for signatures, certificates, and software you did not personally author. Chosen-prefix collisions made it possible to abuse MD5 in public-key certificate contexts historically. You do not need the paper. You need the operational rule: do not use MD5 for security. Do not sign with MD5. Do not treat an MD5 match on an untrusted download as integrity against someone who wanted to swap the file. You will not implement a collision attack in this course.
SHA-1 produces a 160-bit digest (40 hex characters). Collision attacks have been demonstrated (public research and shattered-style examples). Browsers and CAs retired SHA-1 for certificate signatures. New designs should not use SHA-1 for signatures, TLS certificates, or as the only integrity check on untrusted input. Git historically used SHA-1 as an identifier; that is a different conversation (object IDs versus collision-resistant signatures), and this lesson will not turn you into a git-collision researcher. Defender takeaway: if a vendor still publishes only MD5 or only SHA-1 next to an installer, look for SHA-256 (or a signature). If your own app still hashes tokens or files with MD5 “because it is shorter,” that is a design smell, not a performance win you should keep.
2. SHA-256: default for file integrity — still not a password hasher
SHA-256 is part of SHA-2. Digest is 256 bits, 64 hex characters. It is widely used for file integrity (Linux ISO checksums, package manifests), many TLS ecosystem constructions, and general hashing where you need a modern fingerprint. Topic 7’s hash_file.py used it on purpose. Lesson 1 used it on purpose. When you openssl dgst a file you own, -sha256 is the switch that matches that habit. Matching a published SHA-256 still only means byte equality with whatever was hashed to produce the published line. It is not a malware scan. It is not a signature unless a signature is also present. It is the right default for “did these bytes change?” on files you are allowed to hash.
Password hashing is a different family. SHA-256 is fast by design: GPUs and ASICs can evaluate it at enormous rates. That speed is a feature for hashing a disk image. It is a feature for an attacker guessing common passwords against unsalted SHA-256(password) in a stolen table. Next lesson: unique salt, slow KDFs (bcrypt, Argon2, scrypt), pepper as a concept, never MD5(password). Do not walk out of this lesson thinking “we use SHA-256 everywhere, including login.” Do not implement a production password hasher from SHA-256 plus a blog. Do not “compare MD5 vs SHA-256 speed on rockyou” — that is a cracking benchmark, not this syllabus. Your comparison today is three digests of the same lab file you wrote, so you see different widths and different hexes, not a race against a dump.
3. openssl dgst on YOUR file: same bytes, three names, one trusted channel later
OpenSSL’s dgst command hashes a file (or stdin) with a named algorithm and prints the hex. openssl dgst -sha256 yourfile is the CLI twin of hashlib.sha256 on the file bytes. openssl dgst -md5 and -sha1 exist so you can see legacy output — not so you can publish MD5 as your integrity story. Hash the file you created in cyberlium-lab, not /etc/shadow, not a coworker’s disk, not a stolen database. echo -n "string" | openssl dgst hashes a string; for integrity of a download you hash the file. Newlines matter: echo vs echo -n vs a file with a trailing newline are different byte strings and therefore different digests. Record which command you ran in the notes so a later you can reproduce. Python hashlib.md5 / sha1 / sha256 of the same bytes should match openssl if both hashed the same sequence. If they disagree, you hashed different inputs (a path versus contents, a newline, the wrong file).
Broken for collisions is not the same as “MD5 never appears.” Old pipelines still print MD5 as a non-security checksum. Forensics still encounter MD5 in old manifests. Recognizing the hex length (32 vs 40 vs 64) is literacy. Treating MD5 as proof against a motivated attacker is the failure. Never “upgrade” a cert chain by hoping MD5 is fine because your lab file’s MD5 looked unique. Never download a rainbow table to “see MD5 cracks.” Never feed HaveIBeenPwned material into openssl dgst. The wild still has SHA-1 git objects and MD5 CMS fields; your new notes, new checksums, and new designs use SHA-256 for this job, and a password KDF for passwords.
4. Wrong vs right: MD5-as-security vs SHA-256 on YOUR file (password KDF still next)
Worked failure — running all three algorithms is fine; using the broken ones as signatures or as a cracker is not. Right never forges certs or hashes dumps.
Wrong
Ship a new product that signs updates with MD5 or SHA-1. Treat an MD5 match on a shady mirror as integrity. openssl dgst /etc/shadow. Speed-test SHA-256 against a password dump. Download rainbow tables because MD5 is “broken so cracking is the lesson.” Implement a certificate collision. Store passwords as SHA-256 and call this lesson complete. Hash other people’s files to “compare algorithms.”
Right
On a file you wrote in $HOME/cyberlium-lab, print MD5, SHA-1, and SHA-256 with Python and openssl dgst. Record that MD5/SHA-1 are retired for signatures and untrusted integrity; SHA-256 is the integrity default; password hashing is a different family (next lesson). chmod 600 the notes. No dumps, no hashcat, no cert forgery. Next: Password Hashing & Salting.
5. Hands-on: three digests of YOUR file, openssl dgst, lock the notes
Write a short text file you authored. Hash it with hashlib and with openssl dgst for md5, sha1, and sha256. Confirm Python and OpenSSL agree per algorithm. Fill hash-algorithms-notes.txt with the three hexes, digest lengths, the defender rule (no MD5/SHA-1 for signatures or untrusted checksums), and the sentence that password hashing is next and is not raw SHA-256. chmod 600. Do not point dgst at system secret files. Do not add a wordlist.
Command guide
openssl dgst + hashlib on YOUR lab file — compare families, not dumps
DEFENSIVE. YOUR file only. Integrity comparison — NOT cracking. NEVER openssl dgst /etc/shadow. NEVER hashcat. NEVER rainbow tables.
Command — copy this
mkdir -p "$HOME/cyberlium-lab" cd "$HOME/cyberlium-lab"
Command — copy this
printf '%s ' 'cyberlium hash-algorithms demo — my file' > hash-algorithms-demo.txt
Command — copy this
echo "=== openssl dgst (this file only) ===" openssl dgst -md5 hash-algorithms-demo.txt openssl dgst -sha1 hash-algorithms-demo.txt openssl dgst -sha256 hash-algorithms-demo.txt
Command — copy this
cat > hash_algorithms_lab.py << 'PY'
import hashlib
from pathlib import Path
p = Path.home() / "cyberlium-lab" / "hash-algorithms-demo.txt"
data = p.read_bytes()
print("MD5 ", hashlib.md5(data).hexdigest(), "len", 32)
print("SHA-1 ", hashlib.sha1(data).hexdigest(), "len", 40)
print("SHA256", hashlib.sha256(data).hexdigest(), "len", 64)
print("use: SHA-256 for file integrity; MD5/SHA-1 not for signatures/certs")
print("password storage is a DIFFERENT family (bcrypt/Argon2) — next lesson")
PYCommand — copy this
python3 hash_algorithms_lab.py || python hash_algorithms_lab.py
Command — copy this
NOTES="$HOME/cyberlium-lab/hash-algorithms-notes.txt"
{
echo "=== HASH ALGORITHMS (defender literacy, not a cracker) ==="
echo "file: hash-algorithms-demo.txt (I wrote this)"
echo "md5_hex: (32 hex) — collisions practical; do not use for security"
echo "sha1_hex: (40 hex) — collisions demonstrated; retired for certs/signatures"
echo "sha256_hex: (64 hex) — default for file integrity"
echo "openssl_dgst_matched_python: yes/no"
echo "password_hashing: NOT raw SHA-256 — next lesson"
echo "ethics: my file only; no shadow; no HIBP dumps; no rainbow tables; no hashcat"
} > "$NOTES"Command — copy this
chmod 600 "$HOME/cyberlium-lab/hash_algorithms_lab.py" \
"$HOME/cyberlium-lab/hash-algorithms-demo.txt" \
"$NOTES"Windows without chmod: WSL/Git Bash, or restrict files in your profile.
NEVER: openssl dgst -sha256 /etc/shadow NEVER: hashcat -m 0 dumped.txt NEVER: curl a rainbow table or a HaveIBeenPwned dump
Mission: hash-algorithms-notes.txt with openssl dgst of YOUR file (mode 600)
1) Write why MD5 and SHA-1 are retired for signatures/certificates (collision history at defender level — do not produce a collision). 2) Hash a file you created in $HOME/cyberlium-lab with hashlib and openssl dgst (-md5, -sha1, -sha256). Record hexes, lengths, and “SHA-256 default for integrity; password hashing is a different family.” chmod 600 notes and files. 3) Never dgst shadow, never hashcat, never rainbow tables, never other people’s hashes.
Stuck? Ask Cyberlium AI Mentor
If “broken hash means I should crack MD5 dumps” still feels like this lesson, ask for a hint — not hashcat. Try: "Hint only: why are MD5 and SHA-1 retired for signatures and certs, why is SHA-256 the integrity default, why openssl dgst is on MY file, and why password hashing is a different family next?" You still fill the notes. No dumps. No rainbow tables. No certificate forgery.
You can now name three families in the wild: MD5 and SHA-1 as collision-weak for signatures and untrusted integrity, SHA-256 as the file-integrity default you already practiced, and you ran openssl dgst on a file you own so Python and OpenSSL agree. Password storage is still not “just SHA-256.” Next — Password Hashing & Salting — unique per-user salt, slow KDFs (bcrypt, Argon2, scrypt) versus fast SHA-256, pepper as a concept, and why MD5(password) is a museum piece you must not ship. Still conceptual: you will not invent a production hasher from scratch.
Knowledge Check
APPLY: A vendor publishes only an MD5 next to an installer, and a classmate wants to openssl dgst /etc/shadow “to compare real hashes.” What do you prefer for new integrity hashing, and what is out of scope?
Multiple choice
Knowledge Check
APPLY: True or False: Practical collision attacks exist against MD5, so this lesson’s homework is to forge a certificate and download rainbow tables.
True or False
Knowledge Check
APPLY: openssl dgst -sha256 on hash-algorithms-demo.txt matched hashlib. A teammate stores new user passwords as SHA-256 because “we just learned it is modern.” Correct split?
Multiple choice