Every backend touches this daily, yet the precise meanings often aren't clear. Symmetric vs asymmetric? AES vs RSA? Why "encrypt a big file with RSA" doesn't work? This guide covers the core cryptographic primitives without algorithm internals — algorithms are the library's job; ours is to know which tool when.
Symmetric vs Asymmetric — One-Line Difference
Symmetric:
encrypt(plaintext, KEY) = ciphertext
decrypt(ciphertext, KEY) = plaintext
→ same key both ways
Representatives: AES, ChaCha20
Pros: fast (GB/s), small keys
Cons: how do you share the key? Both sides must already have it
────────────────────────────────────
Asymmetric:
encrypt(plaintext, PUBLIC_KEY) = ciphertext
decrypt(ciphertext, PRIVATE_KEY) = plaintext
→ different keys per direction
Representatives: RSA, ECC (Elliptic Curve)
Pros: safe key sharing (public can be public)
Cons: slow (MB/s), big keys (RSA 2048-bit = 256 bytes)
Can only encrypt small data (~hundreds of bytes)Why You Need Both — Hybrid Encryption
Why "RSA-encrypt a 100 MB file" doesn't work:
- RSA encrypt input limit = key size - padding (e.g. 245 bytes)
- 100 MB / 245 B = 408K calls = very slow + inefficient
Fix: hybrid encryption (TLS, GPG, age all use this pattern)
1. Generate a random AES key (32 bytes)
2. Encrypt the 100 MB with AES (fast)
3. Encrypt that AES key with an RSA public key (small, OK)
4. Send encrypted file + encrypted AES key together
Receiver:
1. Decrypt the AES key with RSA private key
2. Decrypt the file with the AES key
→ Best of both — AES's speed + RSA's safe key exchange.Block Cipher Modes — Same Algorithm, Different Result
AES works on 16-byte blocks. Bigger data needs a way to chain blocks; the choice changes the outcome.
ECB — Never Use This
ECB (Electronic Codebook):
Encrypt each block independently
block 1: AES(KEY, block1) = X
block 2: AES(KEY, block2) = Y
...
Problem: same plaintext block → same ciphertext block
→ Patterns are visible (especially in images)
Famous example: ECB-encrypted Tux Linux logo — outline still clearly
visible. Search "ECB penguin" — there it is.CBC, CTR, GCM — Standard Modes
| Mode | Characteristics | Recommendation |
|---|---|---|
| ECB | Independent blocks, reveals patterns | ❌ Never |
| CBC | XOR with previous block, requires IV | ⚠ Legacy (padding-oracle risk) |
| CTR | XOR with a counter (stream-like) | ✓ Fast, parallelizable |
| GCM | CTR + integrity (authentication tag) | ✓✓ Recommended (encrypt + authenticate together) |
| ChaCha20-Poly1305 | Alternative (mobile / low-power CPU) | ✓✓ Recommended |
Modern standard — AES-GCM or ChaCha20-Poly1305. Both AEAD (Authenticated Encryption with Associated Data) — encrypt + integrity together.
Key Derivation — Password → Key
Don't use the user's password "hunter2" directly as an AES key —
too short and low-entropy.
→ Use a KDF (Key Derivation Function) to derive a 32-byte AES key.
PBKDF2 (2000):
output = PBKDF2(password, salt, iterations=600_000, sha256, 32 bytes)
- More iterations = more expensive brute-force
- Salt: same password → different key (per-user randomness)
- Relatively weak against GPUs/ASICs
Argon2 (2015, modern recommendation):
output = Argon2id(password, salt, time=3, memory=64MB, parallelism=4)
- Strong against GPUs/ASICs (memory-hard)
- Successor to bcrypt/scrypt
- Standard for new applications
scrypt (2009):
Similar, memory-hard. Recommended before Argon2.
bcrypt (1999):
Standard for password hashing (not encryption KDF but password storage).At-Rest vs In-Transit
At-Rest: encryption of stored data (DB, files, backups)
- Default cloud DB encryption (AWS RDS, GCP Cloud SQL, etc.)
- Whole-disk encryption (LUKS, BitLocker)
- File-level or application-level encryption
- Keys in KMS / Vault (envelope encryption)
In-Transit: data over the wire
- TLS (HTTPS, SMTPS, IMAPS, DB connections)
- mTLS between services
- VPN, SSH
→ Both needed. At-rest only = network sniffing exposes everything.
→ In-transit only = disk leak exposes everything.Never Roll Your Own Crypto
Common violations:
- "I implemented AES myself" — hard to dodge side channels (timing, power)
- "XOR-based simple encryption" — broken instantly (frequency analysis)
- Custom KDF — too few iterations, missing salt
- Custom random — Math.random() is NOT cryptographically secure
Rules:
- Algorithms: use libraries (libsodium, OpenSSL, Web Crypto API)
- Modes: standard (AES-GCM, ChaCha20-Poly1305)
- Random: crypto.getRandomValues / crypto.randomBytes
- KDF: Argon2 or PBKDF2
Why:
- Algorithms are vetted + standardized by cryptographers
- Side-channel defenses are implementation details
- Home-grown is almost always wrong — Schneier's adage, 25+ years oldRelated Tools
- SHA Hash — SHA-256 etc. (hash, different from encryption — one-way)
- Bcrypt Hash — password hashing (a kind of KDF)
- HMAC Generator — message authentication (integrity, not encryption)
Common Pitfalls
- Using ECB — see above. No modern app should.
- Reusing the same IV / nonce — breaks GCM security. Use a unique random IV per encryption.
- Confusing hash with encryption — SHA-256 is one-way (can't decrypt). Encryption is two-way. Passwords → hash; data protection → encryption.
- Math.random() for keys — predictable. Always crypto.getRandomValues / SecureRandom.
- Encryption without integrity — attackers can tamper (bit flip). Use AEAD like GCM.
Wrap-up
At its core — algorithm internals belong to libraries; our job is picking the right tool. Symmetric vs asymmetric (or hybrid), AEAD modes (GCM / ChaCha20), proper KDF (Argon2 / PBKDF2).
Practical: modern application baseline — AES-256-GCM (or ChaCha20-Poly1305) + Argon2id (password KDF) + TLS 1.3 (in-transit) + cloud KMS (key management). Zero custom code.