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

  1. Setup: Configure development environment
  2. Data: Prepare required data
  3. Implementation: Build core functionality
  4. Testing: Verify correctness
  5. 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

  1. Choose two large primes $p$ and $q$
  2. Compute $n = p \times q$ (modulus)
  3. Compute $\phi(n) = (p-1)(q-1)$ (Euler's totient)
  4. Choose public exponent $e$ (typically 65537)
  5. 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.

Member Exclusive Free Tutorial

This chapter is free exclusive content for registered members! Please login or register to unlock immediately.

Login / Register Now