RSA in Modern Systems: Real Usage and Why It's Being Replaced
1. Why Should You Care?
Youโve learned RSAโs mathematical foundation. But if you look at real systems today:
- TLS 1.3 completely removed RSA key exchange
- Most websites use ECDSA or EdDSA certificates
- SSH defaults to Ed25519 keys
- Signal, WhatsApp use Curve25519
What happened to RSA? Is it obsolete? Understanding RSAโs evolution helps you make better choices for your systems.
2. RSAโs Three Roles
RSA can technically do three things, but modern usage is limited:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Role 1: Direct Encryption โ
โ Status: NEVER USE โ
โ Reason: Size limits, performance, security issues โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Role 2: Key Exchange (Encrypt Session Key) โ
โ Status: DEPRECATED (TLS 1.3 removed it) โ
โ Reason: No forward secrecy โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Role 3: Digital Signatures โ
โ Status: STILL USED (but ECC is preferred) โ
โ Use cases: Code signing, certificates, legacy systems โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ3. Why Not Encrypt Data Directly with RSA
Size Limitation
RSA-2048 can only encrypt:
Maximum raw: 256 bytes
With OAEP padding: 214 bytes
Your 1MB file? Can't do it.
Your 1KB JSON? Still can't.Performance Disaster
# Benchmark: Encrypt 1MB of data
import time
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# Generate keys
rsa_private = rsa.generate_private_key(public_exponent=65537, key_size=2048)
rsa_public = rsa_private.public_key()
aes_key = os.urandom(32)
data = os.urandom(1024 * 1024) # 1MB
# RSA: Must chunk into 190-byte pieces (for safety with padding)
def rsa_encrypt_chunked(data):
chunks = [data[i:i+190] for i in range(0, len(data), 190)]
return [rsa_public.encrypt(
chunk,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
) for chunk in chunks]
# AES-GCM: Encrypt all at once
def aes_encrypt(data):
nonce = os.urandom(12)
return AESGCM(aes_key).encrypt(nonce, data, None)
# Time comparison
start = time.time()
rsa_encrypt_chunked(data)
rsa_time = time.time() - start
start = time.time()
aes_encrypt(data)
aes_time = time.time() - start
print(f"RSA chunked: {rsa_time:.2f}s")
print(f"AES-GCM: {aes_time:.4f}s")
print(f"RSA is {rsa_time/aes_time:.0f}x slower")
# Typical output:
# RSA chunked: 15.23s
# AES-GCM: 0.0021s
# RSA is 7252x slowerThe Right Way: Hybrid Encryption
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def hybrid_encrypt(public_key, plaintext: bytes) -> dict:
"""Encrypt data using hybrid encryption"""
# 1. Generate random symmetric key
session_key = os.urandom(32)
# 2. Encrypt data with symmetric key (fast)
nonce = os.urandom(12)
ciphertext = AESGCM(session_key).encrypt(nonce, plaintext, None)
# 3. Encrypt symmetric key with RSA (small, so fast)
encrypted_key = public_key.encrypt(
session_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
return {
'encrypted_key': encrypted_key,
'nonce': nonce,
'ciphertext': ciphertext
}
def hybrid_decrypt(private_key, encrypted: dict) -> bytes:
"""Decrypt hybrid encrypted data"""
# 1. Decrypt symmetric key with RSA
session_key = private_key.decrypt(
encrypted['encrypted_key'],
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 2. Decrypt data with symmetric key
plaintext = AESGCM(session_key).decrypt(
encrypted['nonce'],
encrypted['ciphertext'],
None
)
return plaintext4. RSA Key Exchange in TLS (The Old Way)
How It Worked in TLS 1.2
Client Server
โ โ
โโโโโโ ClientHello โโโโโโโโโโโโโโโโโ>โ
โ โ
โ<โโโโ ServerHello + Certificate โโโโโ
โ (contains RSA public key) โ
โ โ
โ โ
โ Client generates PreMasterSecret โ
โ Encrypts with server's RSA key โ
โ โ
โโโโโโ ClientKeyExchange โโโโโโโโโโโ>โ
โ (RSA encrypted secret) โ
โ โ
โ Both derive session keys โ
โ from PreMasterSecret โ
โ โ
โ<โโโโ Encrypted Communication โโโโโ>โThe Fatal Flaw: No Forward Secrecy
The Problem:
1. Attacker records all your encrypted traffic (cheap storage)
2. Years later, attacker steals server's private key
3. Attacker decrypts ALL historical traffic
This is called "Harvest Now, Decrypt Later"
RSA key exchange means:
- One key compromise = ALL past sessions compromised
- No forward secrecyTLS 1.3โs Solution: Ephemeral Key Exchange Only
TLS 1.3 removed RSA key exchange entirely.
Only allows (EC)DHE - Ephemeral Diffie-Hellman
Client Server
โ โ
โโโโโโ ClientHello + KeyShare โโโโโโ>โ
โ (ephemeral ECDH public key) โ
โ โ
โ<โโโโ ServerHello + KeyShare โโโโโโโโ
โ (ephemeral ECDH public key) โ
โ โ
โ Both compute shared secret โ
โ Keys are discarded after use โ
โ โ
โ<โโโโ Forward-Secret Encryption โโโ>โ
Even if server's private key leaks later,
past sessions remain secure!5. RSA Signatures Still Exist
While RSA encryption is deprecated, RSA signatures remain:
Current Usage:
โโโ Code Signing
โ โโโ Windows Authenticode (RSA common)
โ โโโ macOS codesign (RSA or ECDSA)
โ โโโ Android APK (transitioning to ECDSA)
โ
โโโ TLS Certificates
โ โโโ Legacy: RSA signatures
โ โโโ Modern: ECDSA preferred
โ โโโ Newest: EdDSA (Ed25519)
โ
โโโ SSH Keys
โ โโโ Legacy: ssh-rsa (deprecated in OpenSSH 8.8)
โ โโโ Modern: rsa-sha2-256, rsa-sha2-512
โ โโโ Preferred: ssh-ed25519
โ
โโโ JWT/JWS
โโโ RS256, RS384, RS512 (RSA)
โโโ ES256, ES384, ES512 (ECDSA, preferred)RSA Signature Example
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
# Generate key pair
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
public_key = private_key.public_key()
# Sign
message = b"Important document content"
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# Verify
try:
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("Signature valid!")
except Exception:
print("Signature invalid!")6. Real-World RSA Vulnerabilities
Padding Oracle Attacks (Bleichenbacherโs Attack)
Attack on PKCS#1 v1.5 padding:
1. Attacker intercepts RSA ciphertext
2. Sends modified ciphertexts to server
3. Server responds differently based on padding validity
4. After millions of queries, attacker recovers plaintext
Famous cases:
- DROWN attack (2016)
- ROBOT attack (2017)
Defense: Use OAEP padding, not PKCS#1 v1.5Timing Attacks
# VULNERABLE: Timing leak in decryption
def vulnerable_decrypt(ciphertext, private_key):
plaintext = rsa_decrypt(ciphertext, private_key)
if not valid_padding(plaintext): # This timing varies!
raise PaddingError()
return plaintext
# SAFE: Constant-time operations
# Use library implementations that handle thisCommon Implementation Mistakes
Mistake 1: Using raw RSA without padding
C = M^e mod n
โ Multiplicative property allows manipulation
โ Attacker can forge: C' = C ร 2^e = (2M)^e
Mistake 2: Using small public exponent with small message
If M^e < n, attacker can just compute e-th root
Mistake 3: Same message to multiple recipients
With e=3 and same M sent to 3 different n values,
Chinese Remainder Theorem recovers M
Mistake 4: Related messages
Encrypt M and M+1 with same key
โ Franklin-Reiter attack recovers both7. RSA vs ECC Comparison
Property | RSA-2048 | ECC P-256
------------------+-----------------+------------------
Key size | 256 bytes | 32 bytes
Signature size | 256 bytes | 64 bytes
Security level | 112 bits | 128 bits
Key generation | Slow (find primes) | Fast
Signing speed | Moderate | Fast
Verification | Fast (small e) | Moderate
Performance trend | Gets worse | Stays good
For equivalent security:
RSA-3072 (384 bytes) โ ECC P-256 (32 bytes)
RSA-15360 (1920 bytes) โ ECC P-521 (66 bytes)8. When to Still Use RSA
Legitimate Use Cases
1. Legacy System Compatibility
- Older systems only support RSA
- Government/enterprise requirements
2. Hardware Constraints
- Some HSMs optimized for RSA
- Some smart cards only support RSA
3. Specific Protocol Requirements
- Certain signed PDF standards
- Some enterprise PKI systemsRecommendations
New Project Checklist:
โก Key exchange: Use ECDH (X25519 preferred)
โก Signatures: Use EdDSA (Ed25519) or ECDSA (P-256)
โก TLS: Use TLS 1.3 (no RSA key exchange)
โก SSH: Use Ed25519 keys
โก JWT: Use ES256 instead of RS256
Only use RSA if:
โก Forced by legacy compatibility
โก Required by external regulations
โก Hardware only supports RSA9. Migration Path from RSA
TLS Certificate Migration
Step 1: Generate new ECC key
openssl ecparam -genkey -name prime256v1 -out ecdsa.key
Step 2: Create CSR
openssl req -new -key ecdsa.key -out ecdsa.csr
Step 3: Deploy dual certificates (transition period)
- Primary: ECDSA certificate
- Fallback: RSA certificate (for old clients)
Step 4: Monitor and remove RSA when safeSSH Key Migration
# Generate new Ed25519 key
ssh-keygen -t ed25519 -C "[email protected]"
# Add to server's authorized_keys
# Keep RSA key temporarily for compatibility
# Test Ed25519 access
ssh -i ~/.ssh/id_ed25519 user@server
# Remove RSA key when confident10. Common Misconceptions
| Misconception | Reality |
|---|---|
| โRSA is brokenโ | RSA math is fine, but there are better alternatives |
| โBigger keys = betterโ | RSA key size growth is unsustainable |
| โRSA encryption is standardโ | Hybrid encryption is the standard |
| โTLS uses RSA encryptionโ | TLS 1.3 uses RSA only for signatures |
| โI should use RSA for securityโ | You should probably use ECC |
11. Summary
Three things to remember:
RSA is not for direct encryption. Use hybrid encryption: RSA encrypts a symmetric key, symmetric key encrypts data.
RSA key exchange lacks forward secrecy. TLS 1.3 removed it. Use ephemeral (EC)DH instead.
RSA signatures still work, but ECC is better. For new projects, prefer Ed25519 or ECDSA over RSA.
12. Whatโs Next
RSA served us well for decades, but its key sizes keep growing while ECC stays compact. How does elliptic curve cryptography achieve the same security with smaller keys?
In the next article: Elliptic Curve Cryptographyโhow points on a curve can replace prime factorization, and why the crypto world is moving to curves.
