Python-RSA

raw JSON →
4.9.1 verified Tue May 12 auth: no python install: verified quickstart: verified maintenance

Python-RSA is a pure-Python implementation of RSA public-key cryptography. It supports key generation, encryption/decryption, and signing/verifying signatures according to PKCS#1 version 1.5. Current version is 4.9.1. The project is effectively in maintenance-only mode — the original author has publicly stated they lack time to actively maintain it, and Snyk classifies its maintenance status as Inactive. Release cadence has been irregular, with no new releases since 4.9.1.

pip install rsa
breaking rsa.verify() returns the hash algorithm name as a string (e.g. 'SHA-256') on success — it does NOT return True. Code checking `if rsa.verify(...) == True` or `if rsa.verify(...)` silently passes even with an unexpected return value.
fix Always wrap rsa.verify() in a try/except rsa.VerificationError block. Do not use its return value as a boolean.
breaking rsa.bigfile (encrypt_bigfile / decrypt_bigfile) and the VARBLOCK format were removed in v4.0. These functions also had serious security flaws: no authenticated encryption, no MACs, and block reordering was possible.
fix Use hybrid encryption: encrypt a random AES key with rsa.encrypt(), then encrypt the payload with AES (e.g. via cryptography or pycryptodome).
breaking PublicKey.save_pkcs1() and PrivateKey.save_pkcs1() always return bytes as of v4.0. Code that previously treated the return value as str will break with a TypeError.
fix Do not decode the returned value; write to files in binary mode: open('key.pem', 'wb').
gotcha All inputs to rsa.encrypt(), rsa.decrypt(), rsa.sign(), and rsa.verify() must be bytes. Passing a Python str raises a TypeError. Encoding is the caller's responsibility.
fix Always encode strings before passing: message.encode('utf-8').
gotcha RSA can only encrypt messages smaller than the key modulus. A 2048-bit key can encrypt at most ~245 bytes (PKCS#1 v1.5 overhead is 11 bytes). Larger payloads raise an OverflowError.
fix Use hybrid encryption for arbitrary-length data: encrypt a random symmetric key with RSA and the data with AES.
gotcha The library is inherently vulnerable to timing attacks because pure-Python integer arithmetic is not constant-time. This is an architectural limitation acknowledged by the maintainer and cannot be patched.
fix For timing-sensitive or high-security production use, prefer cryptography (pyca) or pycryptodome, which use constant-time C extensions.
gotcha Never log or display the stack trace of a rsa.pkcs1.VerificationError or rsa.pkcs1.DecryptionError. The traceback reveals execution path details that leak key information to an attacker.
fix Catch exceptions silently or log only a generic failure message: except rsa.VerificationError: log.warning('Signature check failed') without re-raising or printing the traceback.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.02s 18.9M
3.10 slim (glibc) - - 0.01s 19M
3.11 alpine (musl) - - 0.03s 20.9M
3.11 slim (glibc) - - 0.03s 21M
3.12 alpine (musl) - - 0.03s 12.8M
3.12 slim (glibc) - - 0.02s 13M
3.13 alpine (musl) - - 0.02s 12.4M
3.13 slim (glibc) - - 0.02s 13M
3.9 alpine (musl) - - 0.02s 18.4M
3.9 slim (glibc) - - 0.01s 19M

Generate a key pair, encrypt a message, decrypt it, then sign and verify — the core PKCS#1 v1.5 workflow.

import rsa

# Key generation — use >= 2048 bits in production; 512 shown for speed only
(pub_key, priv_key) = rsa.newkeys(2048)

# Encrypt / Decrypt — input MUST be bytes, not str
message = b'Hello, RSA!'
crypto = rsa.encrypt(message, pub_key)
decrypted = rsa.decrypt(crypto, priv_key)
assert decrypted == message
print('Decrypted:', decrypted.decode('utf-8'))

# Sign / Verify — rsa.verify() returns the hash name (str) on success, NOT True
signature = rsa.sign(message, priv_key, 'SHA-256')
try:
    hash_name = rsa.verify(message, signature, pub_key)
    print('Signature valid, hash method:', hash_name)  # e.g. 'SHA-256'
except rsa.VerificationError:
    print('Signature invalid')

# Persist keys as PEM bytes
pub_pem: bytes = pub_key.save_pkcs1()      # always returns bytes (>=4.0)
priv_pem: bytes = priv_key.save_pkcs1()

# Load keys back
pub_key2 = rsa.PublicKey.load_pkcs1(pub_pem)
priv_key2 = rsa.PrivateKey.load_pkcs1(priv_pem)