Understanding Cryptography: From Caesar to RSA
Cryptography is the science of secure communication. Modern cryptography protects everything from your bank transactions and messaging apps to password storage and software updates. This article covers the three fundamental types of cryptography — symmetric encryption, asymmetric encryption, and hashing — with practical command-line examples using OpenSSL.
Symmetric Encryption with AES
Symmetric encryption uses the same key to encrypt and decrypt data. The Advanced Encryption Standard (AES) is the gold standard, adopted by the US government in 2001 and used worldwide. AES supports key sizes of 128, 192, and 256 bits, with AES-256 providing the highest security level. Symmetric encryption is very fast — hardware-accelerated AES-NI instructions on modern CPUs can encrypt at multiple gigabytes per second — making it ideal for encrypting files, disk volumes, and network traffic (after the key is established via asymmetric cryptography). The main challenge is key distribution: the sender and receiver must share the same secret key through a secure channel.
# Encrypt a file with AES-256-CBC (with salt for key derivation)
openssl enc -aes-256-cbc -salt -in plaintext.txt -out encrypted.enc
# Decrypt the file
openssl enc -d -aes-256-cbc -in encrypted.enc -out decrypted.txt
# You will be prompted for a password, which is derived into the AES key
# using PBKDF2 or similar key derivation function
# Encrypt with a specified key file (256 bits = 32 bytes)
openssl rand -hex 32 > aes_key.hex
openssl enc -aes-256-cbc -salt -in plaintext.txt -out encrypted.enc -pass file:./aes_key.hex
# Benchmark AES speed
openssl speed -evp aes-256-cbc
Note that AES-CBC mode requires an initialization vector (IV) for each encryption. OpenSSL handles this automatically — the IV is randomly generated and stored in the output file alongside the salt. Always use a random IV (never reuse an IV with the same key) to prevent patterns from emerging in the ciphertext. For authenticated encryption that also detects tampering, use AES-GCM instead of AES-CBC.
Asymmetric Encryption with RSA
Asymmetric encryption (also called public-key cryptography) uses a pair of mathematically related keys: a public key that can be shared openly and a private key that must be kept secret. Data encrypted with the public key can only be decrypted with the corresponding private key. This solves the key distribution problem — anyone can encrypt a message using your public key, but only you can decrypt it with your private key. RSA is the most widely known asymmetric algorithm, though Elliptic Curve Cryptography (ECC) is increasingly preferred because it offers equivalent security with much shorter key lengths.
# Generate an RSA private key (2048 bits is the current minimum)
openssl genrsa -out private.pem 2048
# Extract the public key
openssl rsa -in private.pem -pubout -out public.pem
# Encrypt a message with the public key
echo "Secret message" | openssl rsautl -encrypt -pubin -inkey public.pem -out encrypted.msg
# Decrypt with the private key
openssl rsautl -decrypt -inkey private.pem -in encrypted.msg
# Output: Secret message
# Generate a stronger 4096-bit key
openssl genrsa -out private_4096.pem 4096
# Generate an ECC key (more efficient than RSA)
openssl ecparam -genkey -name prime256v1 -out ecc_private.pem
openssl ec -in ecc_private.pem -pubout -out ecc_public.pem
RSA encryption is limited by the key size — you cannot encrypt data larger than the key minus overhead (about 190 bytes for a 2048-bit key). In practice, asymmetric encryption is not used for bulk data. Instead, it is used to encrypt a randomly generated symmetric key (the session key), which is then used with AES to encrypt the actual data. This hybrid approach (called hybrid cryptosystem) combines the key distribution advantages of asymmetric cryptography with the performance of symmetric encryption — it is how TLS/SSL works for every HTTPS connection.
Cryptographic Hashing
A cryptographic hash function takes an input of any size and produces a fixed-size output (the digest or hash) that is effectively unique to that input. Good hash functions are deterministic (same input always produces the same hash), preimage-resistant (given a hash, it is infeasible to find an input that produces it), and collision-resistant (it is infeasible to find two different inputs with the same hash). SHA-256 is the current standard, producing a 256-bit (32-byte) digest. Hashing is used for password storage (never store passwords in plain text), file integrity verification, digital signatures, and blockchain.
# Hash a file
sha256sum document.pdf
# Output: abc123def... document.pdf
# Hash a string
echo -n "hello world" | sha256sum
# Compare checksums to verify file integrity
sha256sum downloaded-file.iso
# Compare with the checksum provided by the publisher
# HMAC (hash-based message authentication code) — keyed hashing
echo -n "message" | openssl dgst -sha256 -hmac "secret_key"
# Password hashing (use bcrypt, argon2, or scrypt — NOT plain SHA)
# Python example:
import hashlib, secrets
password = "user_password"
salt = secrets.token_hex(16)
hash_obj = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000)
print(f"Salt: {salt}")
print(f"Hash: {hash_obj.hex()}")
For password storage, do not use plain SHA-256 — it is too fast and can be brute-forced with consumer GPUs. Instead, use a key derivation function like bcrypt, argon2, or PBKDF2 with a high iteration count (100,000+). These functions are intentionally slow, making brute-force attacks impractical. Always use a unique random salt per password to prevent rainbow table attacks and to ensure that identical passwords produce different hashes.
