Luke a Pro

Luke Sun

Developer & Marketer

๐Ÿ‡บ๐Ÿ‡ฆ

RSA in Modern Systems: Real Usage and Why It's Being Replaced

| , 11 minutes reading.

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 slower

The 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 plaintext

4. 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 secrecy

TLS 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.5

Timing 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 this

Common 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 both

7. 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 systems

Recommendations

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 RSA

9. 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 safe

SSH Key Migration

# Generate new Ed25519 key
ssh-keygen -t ed25519 -C "your_email@example.com"

# 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 confident

10. Common Misconceptions

MisconceptionReality
โ€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:

  1. RSA is not for direct encryption. Use hybrid encryption: RSA encrypts a symmetric key, symmetric key encrypts data.

  2. RSA key exchange lacks forward secrecy. TLS 1.3 removed it. Use ephemeral (EC)DH instead.

  3. 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.