RSA Asymmetric Encryption
๐ฅ Vibe Prompt
"Generate an RSA 2048 key pair. Use the public key to encrypt a file, private key to decrypt."
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
# Generate
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# Save private key
with open('private.pem', 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
# Save public key
with open('public.pem', 'wb') as f:
f.write(public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))
# Encrypt with public key
ciphertext = public_key.encrypt(
b"Secret message",
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
# Decrypt with private key
plaintext = private_key.decrypt(ciphertext, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
print(f"Decrypted: {plaintext.decode()}")
Digital Signature
signature = private_key.sign(
b"Message",
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
# Verify
try:
public_key.verify(signature, b"Message", padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256())
print("โ
Signature verified!")
except:
print("โ Invalid signature!")
Use Cases
- Secure email (PGP)
- SSL/TLS certificates
- Code signing
- SSH authentication
Chapter Summary
- Understand core concepts and principles
- Master implementation methods and techniques
- Familiar with common issues and solutions
- Able to apply in real projects
Further Reading
- Official documentation and API references
- Open source examples on GitHub
- Technical books and online courses
- Community discussions and tech blogs
Implementation Example
Basic Example
# This section provides a complete implementation example
Steps
- Setup: Configure development environment
- Data: Prepare required data
- Implementation: Build core functionality
- Testing: Verify correctness
- Optimization: Improve performance
Common Errors
| Error Type | Cause | Solution | |------------|-------|----------| | Compilation | Syntax | Check code syntax | | Runtime | Environment | Verify dependencies installed | | Logic | Algorithm | Step-by-step debugging | | Performance | Efficiency | Use profilers |
Code Example
import sys
def main():
print("Hello, World!")
if __name__ == "__main__":
main()
References
- Official documentation
- API reference
- Open source examples
- Community discussions
How RSA Works
RSA relies on the mathematical difficulty of factoring large prime numbers.
Key Generation
- Choose two large primes $p$ and $q$
- Compute $n = p \times q$ (modulus)
- Compute $\phi(n) = (p-1)(q-1)$ (Euler's totient)
- Choose public exponent $e$ (typically 65537)
- Compute private exponent $d = e^{-1} \mod \phi(n)$
| Component | Part | Role | |-----------|------|------| | Public key | $(n, e)$ | Encryption, signature verification | | Private key | $(n, d)$ | Decryption, signing |
Encryption and Decryption
$$c = m^e \mod n \quad \text{(encryption)}$$ $$m = c^d \mod n \quad \text{(decryption)}$$
Signing and Verification
$$s = m^d \mod n \quad \text{(signing)}$$ $$m = s^e \mod n \quad \text{(verification)}$$
Python Implementation
Key Generation
from Crypto.PublicKey import RSA
def generate_rsa_keys(key_size: int = 2048):
"""Generate RSA key pair."""
key = RSA.generate(key_size)
private_key = key.export_key('PEM')
public_key = key.publickey().export_key('PEM')
return {
"private_key": private_key.decode(),
"public_key": public_key.decode(),
"key_size": key_size
}
# Generate 2048-bit RSA keys
keys = generate_rsa_keys()
print("\nPrivate Key:")
print(keys["private_key"][:200] + "...")
print("\nPublic Key:")
print(keys["public_key"][:200] + "...")
Encryption and Decryption
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
def rsa_encrypt(plaintext: str, public_key_pem: str) -> str:
"""Encrypt data with RSA public key."""
public_key = RSA.import_key(public_key_pem)
cipher = PKCS1_OAEP.new(public_key)
ciphertext = cipher.encrypt(plaintext.encode())
return base64.b64encode(ciphertext).decode()
def rsa_decrypt(ciphertext_b64: str, private_key_pem: str) -> str:
"""Decrypt data with RSA private key."""
private_key = RSA.import_key(private_key_pem)
cipher = PKCS1_OAEP.new(private_key)
ciphertext = base64.b64decode(ciphertext_b64)
plaintext = cipher.decrypt(ciphertext)
return plaintext.decode()
# Usage
message = "This is a secret message for RSA!"
encrypted = rsa_encrypt(message, keys["public_key"])
decrypted = rsa_decrypt(encrypted, keys["private_key"])
print(f"Original: {message}")
print(f"Encrypted: {encrypted[:50]}...")
print(f"Decrypted: {decrypted}")
Signing and Verification
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
def sign_message(message: str, private_key_pem: str) -> str:
"""Sign a message with RSA private key."""
private_key = RSA.import_key(private_key_pem)
h = SHA256.new(message.encode())
signature = pkcs1_15.new(private_key).sign(h)
return base64.b64encode(signature).decode()
def verify_signature(message: str, signature_b64: str, public_key_pem: str) -> bool:
"""Verify a signature with RSA public key."""
public_key = RSA.import_key(public_key_pem)
h = SHA256.new(message.encode())
signature = base64.b64decode(signature_b64)
try:
pkcs1_15.new(public_key).verify(h, signature)
return True
except (ValueError, TypeError):
return False
# Usage
message = "Transfer $1000 to account 12345"
signature = sign_message(message, keys["private_key"])
valid = verify_signature(message, signature, keys["public_key"])
print(f"Message: {message}")
print(f"Signature valid: {valid}")
# Tampered message fails
tampered = "Transfer $99999 to account 99999"
print(f"Tampered valid: {verify_signature(tampered, signature, keys['public_key'])}")
RSA vs AES Comparison
| Feature | RSA | AES | |---------|-----|-----| | Key type | Asymmetric (public/private) | Symmetric (shared secret) | | Key size | 2048-4096 bits | 128-256 bits | | Speed | Slow (100x slower than AES) | Very fast | | Max data size | Limited by key size | Unlimited (streaming) | | Key distribution | Public key can be shared | Must share securely | | Use case | Key exchange, signatures | Bulk data encryption |
Hybrid Encryption (Best Practice)
1. Generate random AES key (session key)
2. Encrypt session key with RSA public key
3. Encrypt large data with AES-GCM (session key)
4. Send: RSA-encrypted session key + AES ciphertext
def hybrid_encrypt(data: bytes, public_key_pem: str) -> dict:
"""Hybrid encryption: RSA encrypts AES key, AES encrypts data."""
from Crypto.Cipher import AES
import os
# Generate session key
session_key = os.urandom(32) # AES-256
# Encrypt session key with RSA
public_key = RSA.import_key(public_key_pem)
cipher_rsa = PKCS1_OAEP.new(public_key)
enc_session_key = cipher_rsa.encrypt(session_key)
# Encrypt data with AES-GCM
cipher_aes = AES.new(session_key, AES.MODE_GCM)
ciphertext, tag = cipher_aes.encrypt_and_digest(data)
return {
"encrypted_session_key": enc_session_key,
"nonce": cipher_aes.nonce,
"tag": tag,
"ciphertext": ciphertext
}
Summary
RSA provides asymmetric encryption for secure key exchange and digital signatures. It is computationally expensive, so hybrid encryption (RSA + AES) is the standard practice.
Key takeaways:
- RSA: public key encrypts, private key decrypts |
- Private key signs, public key verifies |
- Key size: 2048 bits minimum, 4096 recommended |
- PKCS#1 OAEP is the standard padding scheme for encryption |
- PKCS#1 v1.5 is the standard for digital signatures |
- RSA is slow โ use hybrid encryption for large data |
- RSA key exchange enables secure AES session key sharing |
- ECC (Elliptic Curve Cryptography) is a modern alternative |
What's Next: Hash and HMAC
The next chapter covers hash functions and HMAC.