{"id":285,"library":"rsa","title":"Python-RSA","description":"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.","status":"maintenance","version":"4.9.1","language":"python","source_language":"en","source_url":"https://github.com/sybrenstuvel/python-rsa","tags":["cryptography","rsa","encryption","signing","pkcs1","pure-python","asymmetric"],"install":[{"cmd":"pip install rsa","lang":"bash","label":"Latest stable"}],"dependencies":[{"reason":"Required for ASN.1 encoding/decoding of PEM/DER key structures","package":"pyasn1","optional":false}],"imports":[{"note":"Top-level import gives access to rsa.newkeys(), rsa.encrypt(), rsa.decrypt(), rsa.sign(), rsa.verify()","symbol":"rsa","correct":"import rsa"},{"note":"Public API is re-exported from the top-level rsa package; internal submodule imports are not part of the stable API","wrong":"from rsa.key import PublicKey","symbol":"PublicKey","correct":"from rsa import PublicKey"},{"note":"Same as PublicKey — use the top-level package, not internal submodules","wrong":"from rsa.key import PrivateKey","symbol":"PrivateKey","correct":"from rsa import PrivateKey"},{"note":"Re-exported at top level; catching via internal submodule path is fragile","wrong":"from rsa.pkcs1 import VerificationError","symbol":"VerificationError","correct":"from rsa import VerificationError"},{"note":"Raised by rsa.decrypt() on tampered or invalid ciphertext; catch explicitly rather than broad Exception","symbol":"DecryptionError","correct":"from rsa import DecryptionError"}],"quickstart":{"code":"import rsa\n\n# Key generation — use >= 2048 bits in production; 512 shown for speed only\n(pub_key, priv_key) = rsa.newkeys(2048)\n\n# Encrypt / Decrypt — input MUST be bytes, not str\nmessage = b'Hello, RSA!'\ncrypto = rsa.encrypt(message, pub_key)\ndecrypted = rsa.decrypt(crypto, priv_key)\nassert decrypted == message\nprint('Decrypted:', decrypted.decode('utf-8'))\n\n# Sign / Verify — rsa.verify() returns the hash name (str) on success, NOT True\nsignature = rsa.sign(message, priv_key, 'SHA-256')\ntry:\n    hash_name = rsa.verify(message, signature, pub_key)\n    print('Signature valid, hash method:', hash_name)  # e.g. 'SHA-256'\nexcept rsa.VerificationError:\n    print('Signature invalid')\n\n# Persist keys as PEM bytes\npub_pem: bytes = pub_key.save_pkcs1()      # always returns bytes (>=4.0)\npriv_pem: bytes = priv_key.save_pkcs1()\n\n# Load keys back\npub_key2 = rsa.PublicKey.load_pkcs1(pub_pem)\npriv_key2 = rsa.PrivateKey.load_pkcs1(priv_pem)\n","lang":"python","description":"Generate a key pair, encrypt a message, decrypt it, then sign and verify — the core PKCS#1 v1.5 workflow."},"warnings":[{"fix":"Always wrap rsa.verify() in a try/except rsa.VerificationError block. Do not use its return value as a boolean.","message":"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.","severity":"breaking","affected_versions":"<3.x returned True; >=3.x returns str"},{"fix":"Use hybrid encryption: encrypt a random AES key with rsa.encrypt(), then encrypt the payload with AES (e.g. via cryptography or pycryptodome).","message":"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.","severity":"breaking","affected_versions":"Deprecated in 3.4, removed in 4.0"},{"fix":"Do not decode the returned value; write to files in binary mode: open('key.pem', 'wb').","message":"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.","severity":"breaking","affected_versions":"<4.0 could return str; >=4.0 always returns bytes"},{"fix":"Always encode strings before passing: message.encode('utf-8').","message":"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.","severity":"gotcha","affected_versions":"all"},{"fix":"Use hybrid encryption for arbitrary-length data: encrypt a random symmetric key with RSA and the data with AES.","message":"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.","severity":"gotcha","affected_versions":"all"},{"fix":"For timing-sensitive or high-security production use, prefer cryptography (pyca) or pycryptodome, which use constant-time C extensions.","message":"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.","severity":"gotcha","affected_versions":"all"},{"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.","message":"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.","severity":"gotcha","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-05-12T12:51:02.879Z","next_check":"2026-06-26T00:00:00.000Z","problems":[],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1.2,"disk_size":"18.9M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.01,"mem_mb":1.2,"disk_size":"19M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.3,"disk_size":"20.9M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.3,"disk_size":"21M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1,"disk_size":"12.8M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1,"disk_size":"13M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1.1,"disk_size":"12.4M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1,"disk_size":"13M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1.1,"disk_size":"18.4M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.01,"mem_mb":1.1,"disk_size":"19M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}