基于Python实现S/MIME:如何随加密邮件发送加密AES密钥?
Solution: RSA-Encrypt AES Key and Package Secure Email Content
Got it, let's walk through how to implement the missing RSA encryption part for your AES key, plus packaging everything up to send to the recipient. We'll use Python's cryptography library—it's the de facto standard for secure crypto operations in Python, so it's reliable and well-maintained.
Step 1: Install Dependencies
First, make sure you have the library installed:
pip install cryptography
Step 2: Full Implementation Code
This code covers all your requirements, including the missing RSA encryption for the AES key, plus packaging the payload for transmission. I've included comments to explain each part, and even added a recipient-side decryption/verification function for completeness.
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding, hashes from cryptography.hazmat.primitives.asymmetric import rsa, padding as asymmetric_padding from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key from cryptography.hazmat.backends import default_backend import os import base64 import json # ---------------------- # Helper Functions for Key Loading # ---------------------- def load_rsa_public_key(pem_path): """Load an RSA public key from a PEM file.""" with open(pem_path, "rb") as f: public_key = load_pem_public_key( f.read(), backend=default_backend() ) return public_key def load_rsa_private_key(pem_path, password=None): """Load an RSA private key from a PEM file (supports encrypted keys with password).""" with open(pem_path, "rb") as f: private_key = load_pem_private_key( f.read(), password=password, backend=default_backend() ) return private_key # ---------------------- # AES-256 Encryption/Decryption # ---------------------- def generate_aes256_key(): """Generate a secure random AES-256 key (32 bytes).""" return os.urandom(32) def aes_encrypt(key, plaintext): """Encrypt plaintext using AES-256 in CBC mode (returns IV + ciphertext).""" # Generate a random 16-byte IV (required for CBC mode; IV doesn't need to be secret) iv = os.urandom(16) # Pad plaintext to match AES block size (128 bits) padder = padding.PKCS7(128).padder() padded_data = padder.update(plaintext.encode()) + padder.finalize() # Create and run the cipher cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() # Return IV prepended to ciphertext (needed for decryption) return iv + ciphertext # ---------------------- # RSA Encryption for AES Key # ---------------------- def rsa_encrypt_aes_key(rsa_public_key, aes_key): """Encrypt the AES-256 key using the recipient's RSA public key (OAEP padding for security).""" encrypted_aes_key = rsa_public_key.encrypt( aes_key, asymmetric_padding.OAEP( mgf=asymmetric_padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return encrypted_aes_key # ---------------------- # Digital Signing # ---------------------- def sign_message(private_key, message): """Sign a message using your RSA private key (PSS padding for secure signing).""" signature = private_key.sign( message, asymmetric_padding.PSS( mgf=asymmetric_padding.MGF1(hashes.SHA256()), salt_length=asymmetric_padding.PSS.MAX_LENGTH ), hashes.SHA256() ) return signature # ---------------------- # Main Workflow: Prepare Secure Email Payload # ---------------------- def main(): # 1. Load your private key (for signing) and recipient's public key (for AES key encryption) your_private_key = load_rsa_private_key("your_private_key.pem") recipient_public_key = load_rsa_public_key("recipient_public_key.pem") # 2. Define your email content email_content = "Hello, this is a confidential email that only the recipient can read!" # 3. Generate a one-time AES-256 key (use a new key for every email!) aes_key = generate_aes256_key() # 4. Encrypt the email content with AES-256 encrypted_email = aes_encrypt(aes_key, email_content) # 5. Encrypt the AES key with the recipient's RSA public key encrypted_aes_key = rsa_encrypt_aes_key(recipient_public_key, aes_key) # 6. Sign the original email content (verifies you sent it and content wasn't tampered with) signature = sign_message(your_private_key, email_content.encode()) # 7. Package everything into a sendable format (JSON with base64 encoding, since raw bytes can't be sent directly) payload = { "encrypted_email": base64.b64encode(encrypted_email).decode(), "encrypted_aes_key": base64.b64encode(encrypted_aes_key).decode(), "signature": base64.b64encode(signature).decode() } # Convert to JSON string for transmission (e.g., via email SMTP) sendable_payload = json.dumps(payload, indent=2) print("Payload to send to recipient:\n", sendable_payload) # ---------------------- # Optional: Recipient's Processing Workflow # Uncomment this section to test decryption and signature verification # ---------------------- # def recipient_process(payload_str, recipient_private_key, your_public_key): # payload = json.loads(payload_str) # # Decode base64-encoded fields # encrypted_email = base64.b64decode(payload["encrypted_email"]) # encrypted_aes_key = base64.b64decode(payload["encrypted_aes_key"]) # signature = base64.b64decode(payload["signature"]) # # Decrypt AES key using recipient's private key # aes_key = recipient_private_key.decrypt( # encrypted_aes_key, # asymmetric_padding.OAEP( # mgf=asymmetric_padding.MGF1(algorithm=hashes.SHA256()), # algorithm=hashes.SHA256(), # label=None # ) # ) # # Decrypt email content # iv = encrypted_email[:16] # Extract IV from start of encrypted email # ciphertext = encrypted_email[16:] # unpadder = padding.PKCS7(128).unpadder() # cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend()) # decryptor = cipher.decryptor() # padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize() # plaintext = unpadder.update(padded_plaintext) + unpadder.finalize() # email_content = plaintext.decode() # # Verify signature to ensure authenticity and integrity # try: # your_public_key.verify( # signature, # email_content.encode(), # asymmetric_padding.PSS( # mgf=asymmetric_padding.MGF1(hashes.SHA256()), # salt_length=asymmetric_padding.PSS.MAX_LENGTH # ), # hashes.SHA256() # ) # print("\nRecipient Side Result:") # print("✅ Signature verified successfully!") # print("Decrypted Email Content:", email_content) # except Exception as e: # print("\nRecipient Side Result:") # print("❌ Signature verification failed:", str(e)) # # Load recipient's private key and your public key for testing # recipient_private_key = load_rsa_private_key("recipient_private_key.pem") # your_public_key = load_rsa_public_key("your_public_key.pem") # recipient_process(sendable_payload, recipient_private_key, your_public_key) if __name__ == "__main__": main()
Key Notes for Production Use
- Key Security: Never hardcode private keys in your code. Store them in encrypted vaults or secure environment variables.
- RSA Key Size: Use RSA keys of at least 2048 bits (4096 bits is recommended for long-term security).
- One-Time AES Keys: Always generate a new AES key for every email—this prevents reuse of keys, which is a critical security best practice.
- Error Handling: Add try-except blocks around crypto operations to handle exceptions (e.g., invalid keys, corrupted payloads).
- Padding: We use OAEP for RSA encryption and PSS for signing—these are modern, secure padding schemes (avoid older schemes like PKCS#1 v1.5 unless you have legacy constraints).
内容的提问来源于stack exchange,提问作者Projjol




