Decrypting RSA-Encrypted Session Keys and AES Payloads with Python

Cryptographic Workflow Overview

Modern secure transport protocols frequently employ a hybrid encryption model to balance computational efficiency with secure key distribution. In this architecture, a transient symmetric cipher suite key alongside its Initialization Vector (IV) is generated for each communication session. Rather than transmitting these sensitive parameters openly, they are encapsulated using the receiver's RSA public key. The subsequent application data is then encrypted with the derived symmetric key using a block or stream cipher such as AES. During the reception phase, the workflow inverts: the RSA private key extracts the symmetric credentials, which are subsequently passed to the AES decryption engine to reconstruct the origianl plaintext.

Environment Configuration

The implementation depends on the widely adopted cryptography package. You can provision it in your working environment using the following command:

pip install cryptography

Implementation Strategy

The provided routine demonstrates how to isolate the RSA-wrapped metadata from the encrypted payload, unwrap the symmetric parameters, and process the ciphertext. Encapsulating the logic within a single reusable function improves maintainability and clearly separates the asymmetric retrieval phase from the symmetric decryption phase.

from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.serialization import load_pem_private_key


def recover_and_process_stream(private_key_path: str, captured_data_path: str) -> bytes:
    # 1. Securely load the recipient's RSA private key
    with open(private_key_path, "rb") as key_handle:
        rsa_instance = load_pem_private_key(
            key_handle.read(),
            password=None,
            backend=default_backend()
        )

    # 2. Load the complete transmission buffer
    with open(captured_data_path, "rb") as pkt_handle:
        packet = pkt_handle.read()

    # Define header boundaries (typically 16-byte key + 16-byte IV)
    meta_boundary = 32
    encapsulated_params = packet[:meta_boundary]
    cipher_body = packet[meta_boundary:]

    # 3. Reverse the RSA wrapping using OAEP with SHA-256
    recovered_material = rsa_instance.decrypt(
        encapsulated_params,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )

    # Split the recovered material into cipher key and IV
    aes_key_bytes = recovered_material[:16]
    stream_iv = recovered_material[16:]

    # 4. Initialize AES in CFB mode and decrypt the payload
    cipher_context = Cipher(algorithms.AES(aes_key_bytes), modes.CFB(stream_iv), backend=default_backend())
    decryption_stream = cipher_context.decryptor()
    
    plaintext_output = decryption_stream.update(cipher_body) + decryption_stream.finalize()
    return plaintext_output


if __name__ == "__main__":
    try:
        raw_text = recover_and_process_stream("server_private.pem", "session_capture.bin")
        print(raw_text.decode("utf-8", errors="replace"))
    except ValueError as ve:
        print(f"Incorrect padding or corrupted input: {ve}")
    except Exception as err:
        print(f"Runtime failure: {err}")

This refactored approach modifies the original procedural layout by introducing explicit parameter boundary calculations, dedicated context managers for file I/O, and structured exception handling. The separation of the metadata extraction step ensures that the RSA unboxing operation remains independent from the AES stream processing, which simplifies debugging and facilitates future adaptasions to different cipher modes or padding schemes.

Tags: rsa-oaep aes-cfb-mode key-encapsulation-mechanism cryptography-library tls-hybrid-cipher

Posted on Sat, 16 May 2026 03:26:10 +0000 by gte806e