Install & Compatibility
Where this runs
tested against v4.9.1 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.022s · 18.9MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.8s · import 0.022s · 19MB
17MB installed
● package 17MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
rsa
✓ import rsa
Top-level import gives access to rsa.newkeys(), rsa.encrypt(), rsa.decrypt(), rsa.sign(), rsa.verify()
PublicKey
✓ from rsa import PublicKey
✗ from rsa.key import PublicKey
Public API is re-exported from the top-level rsa package; internal submodule imports are not part of the stable API
PrivateKey
✓ from rsa import PrivateKey
✗ from rsa.key import PrivateKey
Same as PublicKey — use the top-level package, not internal submodules
VerificationError
✓ from rsa import VerificationError
✗ from rsa.pkcs1 import VerificationError
Re-exported at top level; catching via internal submodule path is fragile
DecryptionError
✓ from rsa import DecryptionError
Raised by rsa.decrypt() on tampered or invalid ciphertext; catch explicitly rather than broad Exception
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)
Debug
Known issues
breakingrsa.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.fixAlways wrap rsa.verify() in a try/except rsa.VerificationError block. Do not use its return value as a boolean.
affects: <3.x returned True; >=3.x returns str
breakingrsa.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.fixUse hybrid encryption: encrypt a random AES key with rsa.encrypt(), then encrypt the payload with AES (e.g. via cryptography or pycryptodome).
affects: Deprecated in 3.4, removed in 4.0
breakingPublicKey.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.fixDo not decode the returned value; write to files in binary mode: open('key.pem', 'wb'). affects: <4.0 could return str; >=4.0 always returns bytes
gotchaAll 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.fixAlways encode strings before passing: message.encode('utf-8'). affects: all
gotchaRSA 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.fixUse hybrid encryption for arbitrary-length data: encrypt a random symmetric key with RSA and the data with AES.
affects: all
gotchaThe 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.fixFor timing-sensitive or high-security production use, prefer cryptography (pyca) or pycryptodome, which use constant-time C extensions.
affects: all
gotchaNever 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.fixCatch exceptions silently or log only a generic failure message: except rsa.VerificationError: log.warning('Signature check failed') without re-raising or printing the traceback. affects: all
Upgrade
Version history
4.9.1latest on PyPI · released Apr 16, 2025
Audit
Dependencies
pyasn1requiredRequired for ASN.1 encoding/decoding of PEM/DER key structures