Python RSA-OAEP Code Example (Online Runner)
Python RSA-OAEP examples with hash selection, PEM keys, and hybrid file encryption matching the RSA tools.
Online calculator: use the site RSA tool.
Note: This snippet requires locally installed dependencies and will not run in the online runner.
Calculation method
The RSA tool uses RSA-OAEP with selectable hash functions and PEM keys. The file tool uses a hybrid scheme: AES-256-CBC encrypts the file, and the AES key is RSA-OAEP encrypted into a JSON package.
Install the dependency first: pip install pycryptodome.
Implementation notes
- Package:
pycryptodomeprovides RSA key handling and OAEP padding. - Implementation: OAEP uses the selected hash (SHA-1/256/384/512). File encryption uses a random AES-256 key + IV, then wraps the AES key using RSA-OAEP and stores everything in a JSON payload.
- Notes: RSA-OAEP has a maximum message size (
key_bytes - 2*hash_len - 2), so encrypt small payloads only. Use RSA for keys and AES for bulk data, as shown here.
python
from __future__ import annotations
import base64
import json
from pathlib import Path
from typing import Literal
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Hash import SHA1, SHA256, SHA384, SHA512
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
HashAlg = Literal["SHA-1", "SHA-256", "SHA-384", "SHA-512"]
CipherEncoding = Literal["hex", "base64"]
HASH_MAP = {
"SHA-1": SHA1,
"SHA-256": SHA256,
"SHA-384": SHA384,
"SHA-512": SHA512,
}
def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")
def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)
def generate_rsa_keypair(key_size: int = 2048) -> tuple[str, str]:
key = RSA.generate(key_size)
public_pem = key.publickey().export_key().decode("ascii")
private_pem = key.export_key().decode("ascii")
return public_pem, private_pem
def rsa_encrypt(text: str, public_pem: str, hash_alg: HashAlg = "SHA-256", encoding: CipherEncoding = "base64") -> str:
key = RSA.import_key(public_pem)
cipher = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
ciphertext = cipher.encrypt(text.encode("utf-8"))
return _encode_ciphertext(ciphertext, encoding)
def rsa_decrypt(ciphertext: str, private_pem: str, hash_alg: HashAlg = "SHA-256", encoding: CipherEncoding = "base64") -> str:
key = RSA.import_key(private_pem)
cipher = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
plain = cipher.decrypt(_decode_ciphertext(ciphertext, encoding))
return plain.decode("utf-8", errors="replace")
def _pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes, block_size: int = 16) -> bytes:
if not data:
return data
pad_len = data[-1]
if pad_len < 1 or pad_len > block_size:
raise ValueError("Invalid PKCS7 padding")
if data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("Invalid PKCS7 padding")
return data[:-pad_len]
def rsa_encrypt_file(path: Path, public_pem: str, hash_alg: HashAlg = "SHA-256") -> str:
data = path.read_bytes()
aes_key = get_random_bytes(32)
iv = get_random_bytes(16)
aes = AES.new(aes_key, AES.MODE_CBC, iv=iv)
ciphertext = aes.encrypt(_pkcs7_pad(data))
key = RSA.import_key(public_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
encrypted_key = cipher_rsa.encrypt(aes_key)
payload = {
"version": 1,
"hash": hash_alg,
"mode": "AES-256-CBC",
"iv": iv.hex(),
"encryptedKey": base64.b64encode(encrypted_key).decode("ascii"),
"ciphertext": base64.b64encode(ciphertext).decode("ascii"),
"originalName": path.name,
"mimeType": "application/octet-stream",
}
return json.dumps(payload, indent=2)
def rsa_decrypt_file(package_json: str, private_pem: str, fallback_hash: HashAlg = "SHA-256") -> bytes:
payload = json.loads(package_json)
hash_alg = payload.get("hash") if payload.get("hash") in HASH_MAP else fallback_hash
key = RSA.import_key(private_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
aes_key = cipher_rsa.decrypt(base64.b64decode(payload["encryptedKey"]))
aes = AES.new(aes_key, AES.MODE_CBC, iv=bytes.fromhex(payload["iv"]))
plaintext = aes.decrypt(base64.b64decode(payload["ciphertext"]))
return _pkcs7_unpad(plaintext)
# Example usage
public_pem, private_pem = generate_rsa_keypair(2048)
cipher = rsa_encrypt("hello", public_pem, hash_alg="SHA-256", encoding="base64")
print(cipher)
plain = rsa_decrypt(cipher, private_pem, hash_alg="SHA-256", encoding="base64")
print(plain)
File encryption example
python
from __future__ import annotations
import base64
import json
from pathlib import Path
import tempfile
from typing import Literal
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Hash import SHA1, SHA256, SHA384, SHA512
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
HashAlg = Literal["SHA-1", "SHA-256", "SHA-384", "SHA-512"]
HASH_MAP = {
"SHA-1": SHA1,
"SHA-256": SHA256,
"SHA-384": SHA384,
"SHA-512": SHA512,
}
def generate_rsa_keypair(key_size: int = 2048) -> tuple[str, str]:
key = RSA.generate(key_size)
public_pem = key.publickey().export_key().decode("ascii")
private_pem = key.export_key().decode("ascii")
return public_pem, private_pem
def _pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes, block_size: int = 16) -> bytes:
if not data:
return data
pad_len = data[-1]
if pad_len < 1 or pad_len > block_size:
raise ValueError("Invalid PKCS7 padding")
if data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("Invalid PKCS7 padding")
return data[:-pad_len]
def rsa_encrypt_file(path: Path, public_pem: str, hash_alg: HashAlg = "SHA-256") -> str:
data = path.read_bytes()
aes_key = get_random_bytes(32)
iv = get_random_bytes(16)
aes = AES.new(aes_key, AES.MODE_CBC, iv=iv)
ciphertext = aes.encrypt(_pkcs7_pad(data))
key = RSA.import_key(public_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
encrypted_key = cipher_rsa.encrypt(aes_key)
payload = {
"version": 1,
"hash": hash_alg,
"mode": "AES-256-CBC",
"iv": iv.hex(),
"encryptedKey": base64.b64encode(encrypted_key).decode("ascii"),
"ciphertext": base64.b64encode(ciphertext).decode("ascii"),
"originalName": path.name,
"mimeType": "application/octet-stream",
}
return json.dumps(payload, indent=2)
def rsa_decrypt_file(package_json: str, private_pem: str, fallback_hash: HashAlg = "SHA-256") -> bytes:
payload = json.loads(package_json)
hash_alg = payload.get("hash") if payload.get("hash") in HASH_MAP else fallback_hash
key = RSA.import_key(private_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
aes_key = cipher_rsa.decrypt(base64.b64decode(payload["encryptedKey"]))
aes = AES.new(aes_key, AES.MODE_CBC, iv=bytes.fromhex(payload["iv"]))
plaintext = aes.decrypt(base64.b64decode(payload["ciphertext"]))
return _pkcs7_unpad(plaintext)
with tempfile.TemporaryDirectory() as temp_dir:
sample_path = Path(temp_dir) / "sample.bin"
sample_path.write_bytes(b"hello")
public_pem, private_pem = generate_rsa_keypair(2048)
package_json = rsa_encrypt_file(sample_path, public_pem, hash_alg="SHA-256")
restored = rsa_decrypt_file(package_json, private_pem)
print(restored)Complete script (implementation + tests)
python
from __future__ import annotations
import base64
import json
from typing import Literal
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Hash import SHA1, SHA256, SHA384, SHA512
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
HashAlg = Literal["SHA-1", "SHA-256", "SHA-384", "SHA-512"]
CipherEncoding = Literal["hex", "base64"]
HASH_MAP = {
"SHA-1": SHA1,
"SHA-256": SHA256,
"SHA-384": SHA384,
"SHA-512": SHA512,
}
def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")
def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)
def generate_rsa_keypair(key_size: int = 2048) -> tuple[str, str]:
key = RSA.generate(key_size)
public_pem = key.publickey().export_key().decode("ascii")
private_pem = key.export_key().decode("ascii")
return public_pem, private_pem
def rsa_encrypt(text: str, public_pem: str, hash_alg: HashAlg = "SHA-256", encoding: CipherEncoding = "base64") -> str:
key = RSA.import_key(public_pem)
cipher = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
ciphertext = cipher.encrypt(text.encode("utf-8"))
return _encode_ciphertext(ciphertext, encoding)
def rsa_decrypt(ciphertext: str, private_pem: str, hash_alg: HashAlg = "SHA-256", encoding: CipherEncoding = "base64") -> str:
key = RSA.import_key(private_pem)
cipher = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
plain = cipher.decrypt(_decode_ciphertext(ciphertext, encoding))
return plain.decode("utf-8", errors="replace")
def _pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes, block_size: int = 16) -> bytes:
if not data:
return data
pad_len = data[-1]
if pad_len < 1 or pad_len > block_size:
raise ValueError("Invalid PKCS7 padding")
if data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("Invalid PKCS7 padding")
return data[:-pad_len]
def rsa_encrypt_file(path, public_pem: str, hash_alg: HashAlg = "SHA-256") -> str:
data = path.read_bytes()
aes_key = get_random_bytes(32)
iv = get_random_bytes(16)
aes = AES.new(aes_key, AES.MODE_CBC, iv=iv)
ciphertext = aes.encrypt(_pkcs7_pad(data))
key = RSA.import_key(public_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
encrypted_key = cipher_rsa.encrypt(aes_key)
payload = {
"version": 1,
"hash": hash_alg,
"mode": "AES-256-CBC",
"iv": iv.hex(),
"encryptedKey": base64.b64encode(encrypted_key).decode("ascii"),
"ciphertext": base64.b64encode(ciphertext).decode("ascii"),
"originalName": path.name,
"mimeType": "application/octet-stream",
}
return json.dumps(payload, indent=2)
def rsa_decrypt_file(package_json: str, private_pem: str, fallback_hash: HashAlg = "SHA-256") -> bytes:
payload = json.loads(package_json)
hash_alg = payload.get("hash") if payload.get("hash") in HASH_MAP else fallback_hash
key = RSA.import_key(private_pem)
cipher_rsa = PKCS1_OAEP.new(key, hashAlgo=HASH_MAP[hash_alg])
aes_key = cipher_rsa.decrypt(base64.b64decode(payload["encryptedKey"]))
aes = AES.new(aes_key, AES.MODE_CBC, iv=bytes.fromhex(payload["iv"]))
plaintext = aes.decrypt(base64.b64decode(payload["ciphertext"]))
return _pkcs7_unpad(plaintext)
def run_tests() -> None:
public_pem, private_pem = generate_rsa_keypair(2048)
cipher = rsa_encrypt("hello", public_pem, hash_alg="SHA-256", encoding="hex")
recovered = rsa_decrypt(cipher, private_pem, hash_alg="SHA-256", encoding="hex")
assert recovered == "hello"
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as temp_dir:
sample_path = Path(temp_dir) / "sample.bin"
sample_path.write_bytes(b"hello")
payload = rsa_encrypt_file(sample_path, public_pem, hash_alg="SHA-256")
restored = rsa_decrypt_file(payload, private_pem)
assert restored == b"hello"
print("RSA tests passed")
if __name__ == "__main__":
run_tests()