Encryption Basics
Encryption is the foundation of data protection. Every engineer needs to understand when to use symmetric encryption, asymmetric encryption, and hashing -- not the math behind them, but the practical decisions that determine whether your data stays secure. The most common encryption failures are not cryptographic breakthroughs. They are engineering mistakes: using the wrong algorithm, mishandling keys, or rolling your own crypto.
Symmetric Encryption
Symmetric encryption uses a single key for both encryption and decryption. It is fast and efficient for large amounts of data.
AES-256-GCM
AES (Advanced Encryption Standard) with 256-bit keys in GCM (Galois/Counter Mode) is the current standard for symmetric encryption. GCM provides both confidentiality and authenticity -- it encrypts the data and produces an authentication tag that detects tampering.
Plaintext + Key + Nonce → AES-256-GCM → Ciphertext + Auth Tag
Ciphertext + Key + Nonce + Auth Tag → AES-256-GCM Decrypt → Plaintext
Key properties of AES-256-GCM:
- 256-bit key: 2^256 possible keys. Brute force is not a realistic threat.
- 96-bit nonce (IV): Must be unique for every encryption operation with the same key. Reusing a nonce with the same key completely breaks GCM security.
- Authentication tag: Detects any modification to the ciphertext. Reject data if the tag does not verify.
# Python example using the cryptography library
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12) # 96-bit nonce, must be unique per encryption
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data)
When to Use Symmetric Encryption
- Encrypting data at rest (files, database fields, backups)
- Encrypting data in transit when both sides share a key (often negotiated via asymmetric encryption first)
- Disk encryption (LUKS, BitLocker, FileVault all use AES)
- Any scenario where a single party controls both encryption and decryption
Asymmetric Encryption
Asymmetric encryption uses a key pair: a public key that anyone can have and a private key that must stay secret. Data encrypted with the public key can only be decrypted with the private key. Asymmetric encryption is slow compared to symmetric -- roughly 1000x slower for RSA.
RSA
RSA is the most widely deployed asymmetric algorithm. Use a minimum key size of 2048 bits, preferably 4096 bits.
# Generate an RSA key pair
openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -pubout -out public.pem
RSA is used for key exchange (encrypting a symmetric key to send it securely), digital signatures (signing data to prove authenticity), and TLS handshakes.
Ed25519
Ed25519 is a modern elliptic curve algorithm that produces smaller keys and faster signatures than RSA. It is the recommended choice for new implementations of digital signatures and SSH keys.
# Generate an Ed25519 SSH key
ssh-keygen -t ed25519 -C "your_email@example.com"
Ed25519 advantages over RSA:
- 32-byte keys vs 256-512 byte RSA keys
- Faster signing and verification
- No padding oracle attacks
- Deterministic signatures (same input always produces same signature)
When to Use Asymmetric Encryption
- TLS handshakes (negotiate a symmetric session key)
- Digital signatures (code signing, JWTs, document signing)
- SSH authentication
- Any scenario where two parties need to communicate securely without sharing a secret in advance
Hashing
A hash function takes arbitrary input and produces a fixed-size output. It is a one-way function -- you cannot recover the input from the hash. Hashing is not encryption. There is no key and no way to reverse it.
SHA-256
SHA-256 produces a 256-bit (32-byte) hash. It is used for data integrity verification, not for passwords.
$ echo -n "hello" | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Use SHA-256 for: file integrity checks, content addressing (like Git commits), HMAC construction, and digital signatures.
bcrypt
bcrypt is designed specifically for password hashing. It is intentionally slow and includes a salt automatically. The work factor (cost) can be increased over time as hardware gets faster.
# bcrypt hash with cost factor 12
$2b$12$LJ3m4ys3Lk0TSwHjnF4Mj.FQQnKsyToi3VPEfGCxR1AIQK6MuMWy
Argon2
Argon2 is the winner of the 2015 Password Hashing Competition. It is the current best practice for password hashing. Argon2id (the recommended variant) is resistant to both GPU attacks and side-channel attacks.
# Argon2id parameters
- Memory: 64 MB minimum (makes GPU attacks expensive)
- Iterations: 3 minimum
- Parallelism: 1-4 threads
When to Use Each Hash
- SHA-256: File integrity, checksums, HMACs, digital signatures. Never for passwords.
- bcrypt: Password hashing. Widely supported, battle-tested. Cost factor 12+ recommended.
- Argon2id: Password hashing when you can choose freely. Memory-hard, the modern standard. Preferred over bcrypt for new projects.
The Decision Framework
What are you protecting? Use this:
─────────────────────────────────────────────────────────
Data at rest (files, DB fields) AES-256-GCM
Data in transit TLS 1.3 (handles crypto for you)
Passwords Argon2id or bcrypt
File integrity SHA-256
Digital signatures Ed25519 or RSA
Key exchange X25519 or RSA
SSH keys Ed25519
Do Not Implement Crypto Yourself
This is the single most important rule in applied cryptography. Use well-maintained libraries:
- Python:
cryptographylibrary (notpycrypto, which is abandoned) - Node.js: built-in
cryptomodule orlibsodium - Go:
crypto/aes,crypto/cipher,golang.org/x/crypto - Java: Tink (by Google) or Bouncy Castle
- Any language: libsodium/NaCl provides safe high-level APIs
Every custom crypto implementation you write is a liability. The Adobe breach of 2013 exposed 153 million user records partly because Adobe used ECB mode for AES encryption -- a mode that preserves patterns in the plaintext. The encrypted passwords were effectively a substitution cipher. Using AES-GCM or any standard library would have prevented this.
Real-World Crypto Failures
Adobe (2013)
Adobe encrypted passwords with 3DES in ECB mode using a single key for all users. ECB mode encrypts identical plaintext blocks to identical ciphertext blocks, so users with the same password had the same encrypted value. Combined with password hints stored in plaintext, attackers could deduce passwords through pattern analysis. 153 million accounts were compromised.
Heartbleed (2014)
A buffer over-read bug in OpenSSL allowed attackers to read server memory, including private keys. The bug was in the TLS heartbeat extension implementation -- a bounds check was missing. This was not a crypto flaw but an implementation flaw in a crypto library. It demonstrated that even widely-used crypto libraries can have critical bugs.
Sony PlayStation Network (2011)
Sony used a static value where a random nonce was required in their ECDSA implementation for PS3 code signing. With a known nonce, the private key can be computed from a single signature. Attackers extracted Sony's private signing key and could sign arbitrary code to run on any PS3. A random number generator call would have prevented this entirely.
Common Pitfalls
- Using ECB mode. ECB encrypts each block independently, preserving patterns. Always use GCM, CBC with HMAC, or another authenticated mode.
- Reusing nonces with AES-GCM. A nonce must never repeat with the same key. Use random 96-bit nonces or a counter. In high-volume systems, random nonces can collide -- use a deterministic approach.
- Hashing passwords with SHA-256. SHA-256 is fast. That is a problem for passwords. An attacker can compute billions of SHA-256 hashes per second. Use bcrypt or Argon2id.
- Using MD5 or SHA-1. Both have known collision attacks. MD5 collisions can be generated in seconds. SHA-1 was practically broken by Google's SHAttered attack in 2017. Use SHA-256 or SHA-3.
- Storing encryption keys alongside encrypted data. This defeats the purpose of encryption. If an attacker gets the data, they get the key too. Separate key storage from data storage.
- Rolling your own crypto protocol. Even correct primitives can be combined insecurely. Use TLS for transport, use a KMS for key management, use a library for everything else.
- Ignoring authentication tags. AES-GCM produces an authentication tag. If you do not verify it, an attacker can modify the ciphertext without detection. Always verify before decrypting.
Key Takeaways
- Symmetric encryption (AES-256-GCM) is fast and uses one key. Use it for data at rest and bulk encryption.
- Asymmetric encryption (RSA, Ed25519) uses key pairs and is slow. Use it for key exchange, signatures, and TLS handshakes.
- Hashing (SHA-256) is one-way and fast. Use it for integrity, never for passwords.
- Password hashing (Argon2id, bcrypt) is intentionally slow. Use it exclusively for passwords.
- Never implement your own cryptographic algorithms or protocols. Use established libraries.
- The most common crypto breaches come from wrong mode selection (ECB), nonce reuse, and using fast hashes for passwords.
- Ed25519 is preferred over RSA for new projects due to smaller keys and stronger security properties.