Cryptography is the invisible infrastructure of cybersecurity. Every HTTPS connection, every password hash, every encrypted file, every digital signature relies on mathematical algorithms that have been carefully designed and even more carefully implemented. But cryptography breaks in unexpected ways — often not because the math fails, but because of implementation mistakes, weak keys, deprecated algorithms, or misunderstood assumptions. This guide covers what every security practitioner needs to understand.
Hashing: Not Encryption
Hashing and encryption are completely different. Encryption is reversible (with the right key). Hashing is a one-way function — given a hash, you cannot mathematically recover the original input (you can only guess inputs and compare). This is why passwords should be hashed, not encrypted.
# Common hash algorithms and their status:
# MD5 — BROKEN, do not use for security (collision attacks possible)
echo -n "password" | md5sum
# → 5f4dcc3b5aa765d61d8327deb882cf99
# SHA-1 — DEPRECATED, avoid for new implementations
echo -n "password" | sha1sum
# → 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
# SHA-256 — SAFE for most purposes (file integrity, digital signatures)
echo -n "password" | sha256sum
# → 5e884898da280471...
# For PASSWORD STORAGE — use adaptive hash functions, never raw SHA:
# bcrypt, scrypt, or Argon2id (current best practice)
python3 -c "import bcrypt; print(bcrypt.hashpw(b'password', bcrypt.gensalt(rounds=12)))"
Why Raw SHA for Passwords Is Wrong
# GPU cracking speed comparison (Hashcat benchmarks):
# MD5: 64,000,000,000 hashes/second (64 billion/sec on modern GPU)
# SHA-256: 14,000,000,000 hashes/second
# bcrypt (cost 12): 10,000 hashes/second
# Argon2id: 1,000 hashes/second
# With 10B MD5 guesses/sec, "password123" is cracked in milliseconds
# With bcrypt cost 12, same guess takes 100,000+ seconds
# The slowness is the feature — adaptive cost functions are designed
# to be computationally expensive to resist brute force
Symmetric Encryption
Symmetric encryption uses the same key to encrypt and decrypt. AES (Advanced Encryption Standard) is the gold standard and is used in TLS, disk encryption, and file encryption worldwide.
# AES encryption modes — the mode matters enormously:
# AES-ECB — NEVER USE — same plaintext block always produces same ciphertext block
# AES-CBC — widely used but requires careful IV management
# AES-GCM — RECOMMENDED — authenticated encryption, detects tampering
# Python example: AES-GCM encryption (correct)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
key = os.urandom(32) # 256-bit key, generated securely
nonce = os.urandom(12) # 96-bit nonce, never reuse with same key!
plaintext = b"Secret data"
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, None) # includes auth tag
decrypted = aesgcm.decrypt(nonce, ciphertext, None)
# Raises InvalidTag exception if ciphertext was tampered with
Asymmetric Encryption and PKI
Asymmetric cryptography uses a key pair: a public key (share freely) and a private key (never share). Data encrypted with the public key can only be decrypted with the private key. Data signed with the private key can be verified by anyone with the public key.
# Generate RSA key pair:
openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -pubout -out public.pem
# Encrypt a file with public key:
openssl rsautl -encrypt -pubin -inkey public.pem -in secret.txt -out secret.enc
# Decrypt with private key:
openssl rsautl -decrypt -inkey private.pem -in secret.enc -out decrypted.txt
# Digital signatures — prove authenticity and integrity:
openssl dgst -sha256 -sign private.pem -out signature.bin document.pdf
openssl dgst -sha256 -verify public.pem -signature signature.bin document.pdf
# Modern alternative: Ed25519 (faster, shorter keys, no padding issues)
openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
TLS: How HTTPS Actually Works
# Check TLS configuration of a server:
openssl s_client -connect target.com:443 -tls1_2
# Look for: Certificate chain, cipher suite, TLS version
# Check for weak ciphers with testssl.sh:
bash testssl.sh https://target.com
# Flags: SSLv3, TLS 1.0, TLS 1.1 — all should be DISABLED
# Check: BEAST, POODLE, HEARTBLEED, ROBOT vulnerabilities
# Check certificate expiry:
echo | openssl s_client -connect target.com:443 2>/dev/null | openssl x509 -noout -dates
# Common TLS vulnerabilities:
# POODLE — SSLv3 with CBC mode, forces protocol downgrade
# Heartbleed — OpenSSL buffer over-read leaks server memory
# BEAST — TLS 1.0 CBC cipher attack
# ROBOT — RSA PKCS#1 v1.5 padding oracle attack
Common Cryptographic Mistakes to Watch For
- Using ECB mode — patterns in plaintext remain visible in ciphertext. The “ECB penguin” demonstrates this perfectly.
- Reusing IVs/nonces — in GCM mode, nonce reuse with the same key completely breaks confidentiality and authentication
- Rolling your own crypto — custom encryption algorithms almost always have flaws. Use battle-tested libraries.
- Hardcoded keys — encryption keys in source code are extractable by anyone who has the binary
- Incorrect random number generation — using
rand()orMath.random()for security tokens — these are not cryptographically secure. Useos.urandom()in Python orcrypto.randomBytes()in Node.js - Short keys — RSA-1024 is considered weak. Minimum RSA-2048, prefer RSA-4096 or switch to ECC