cryptography
The cryptography library (pyca/cryptography) provides cryptographic recipes and primitives to Python developers, aiming to be a 'cryptographic standard library'. It offers both high-level recipes (e.g., Fernet symmetric encryption) and low-level hazmat (hazardous materials) primitives covering symmetric ciphers, asymmetric algorithms (RSA, EC, DSA), message digests, KDFs, X.509, and more. Current stable version is 46.0.6 (released 2026-03-25). The project releases frequently—typically multiple times per major version cycle—with major versions arriving several times per year.
Warnings
- breaking The `backend` parameter was removed from all hazmat constructors (Cipher, rsa.generate_private_key, ec.generate_private_key, etc.). Passing `backend=default_backend()` now raises TypeError.
- breaking `signer()` and `verifier()` methods on public/private key objects were removed in 44.0.0 after being deprecated since 2.0.
- breaking OpenSSL 1.1.x support was removed; OpenSSL 3.0.0 or later is now required when building from source. LibreSSL < 4.1 also dropped.
- breaking Loading keys with unsupported algorithms or explicit curve encodings now raises `UnsupportedAlgorithm` instead of `ValueError`.
- deprecated CFB, OFB, and CFB8 modes have been moved to 'Decrepit cryptography' and deprecated in `cryptography.hazmat.primitives.ciphers.modes`. They will be removed in 49.0.0. Camellia cipher is similarly deprecated.
- gotcha ECB mode (`modes.ECB`) is available but insecure—it encrypts identical plaintext blocks to identical ciphertext blocks, leaking data patterns. The library does not prevent its use.
- gotcha When building from source (not from a wheel), a Rust toolchain (cargo) is required. On Alpine Linux < 3.21 and older Debian/Ubuntu, the default Rust is too old.
Install
-
pip install cryptography -
pip install --upgrade pip && pip install cryptography
Imports
- Fernet
from cryptography.fernet import Fernet
- Cipher, algorithms, modes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
- AESGCM (AEAD shortcut)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
- rsa.generate_private_key
from cryptography.hazmat.primitives.asymmetric import rsa
- padding (asymmetric)
from cryptography.hazmat.primitives.asymmetric import padding
- hashes
from cryptography.hazmat.primitives import hashes
- serialization
from cryptography.hazmat.primitives import serialization
- x509
from cryptography import x509
Quickstart
# --- High-level: Fernet (recommended for most use cases) ---
from cryptography.fernet import Fernet
key = Fernet.generate_key() # Must be stored securely; bytes
f = Fernet(key)
token = f.encrypt(b"secret message") # Returns URL-safe base64 token
plaintext = f.decrypt(token) # Raises InvalidToken if tampered
assert plaintext == b"secret message"
# --- Low-level: AES-GCM via hazmat (authenticated encryption) ---
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
aes_key = AESGCM.generate_key(bit_length=256) # 32 random bytes
aesgcm = AESGCM(aes_key)
nonce = os.urandom(12) # 96-bit nonce; NEVER reuse with same key
ciphertext = aesgcm.encrypt(nonce, b"secret data", b"optional AAD")
decrypted = aesgcm.decrypt(nonce, ciphertext, b"optional AAD")
assert decrypted == b"secret data"
# --- RSA key generation & sign/verify ---
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
message = b"message to sign"
signature = private_key.sign(
message,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
public_key.verify(
signature, message,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
) # Raises InvalidSignature if verification fails
print("All operations succeeded")