# pyca/cryptography: Python Cryptographic Recipes and Primitives

> cryptography is a Python package that provides both high-level recipes for common tasks such as symmetric encryption and a low-level interface to algorithms including symmetric ciphers, message digests, and key derivation functions. It targets Python 3.9 and later and uses a Rust backend over OpenSSL.

**pyca/cryptography** — cryptography is a package designed to expose cryptographic primitives and recipes to Python developers.

- Repository: https://github.com/pyca/cryptography
- Website: https://cryptography.io
- Stars: 7,791 · Forks: 1,825
- Language: Python
- License: NOASSERTION
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pyca-cryptography

## What cryptography Solves and Who It Is For

Python's standard library provides hashlib for digests and hmac for message authentication codes, but it does not ship a general-purpose cryptographic toolkit. Before packages like cryptography, Python developers had to bind to OpenSSL directly, use PyCrypto (which is unmaintained), or rely on platform-specific libraries. The result was fragmented, error-prone code.

The README describes cryptography's goal as becoming the cryptographic standard library for Python. It targets Python developers who need to encrypt data, verify signatures, handle X.509 certificates, or derive keys from passwords. The package is not aimed at cryptographers designing new algorithms: it exposes well-established algorithms through a stable API.

The package serves a wide audience: web frameworks that need TLS certificate inspection, application developers building end-to-end encrypted storage, and security tooling that parses public-key infrastructure. It supports Python 3.9 and later and PyPy3 7.3.11 and later.

## The Split Between High-Level Recipes and Hazardous Materials

The package is divided into two layers. The high-level layer, called recipes, provides simple, opinionated APIs for common tasks. The most prominent is Fernet, which implements authenticated symmetric encryption using AES-128-CBC with an HMAC-SHA256 signature. A caller generates a key, encrypts, and decrypts in three lines without choosing cipher modes, key sizes, or padding schemes.

The low-level layer is explicitly labelled hazardous materials (hazmat) in the package namespace. The hazmat module exposes individual cryptographic primitives: specific cipher modes, hash algorithms, asymmetric key types, and key derivation functions such as PBKDF2, scrypt, and HKDF. The intent is that engineers who need to implement a specific protocol or interoperate with an existing system can reach these primitives, while the hazmat name serves as a reminder that misuse of low-level cryptography is a common source of security vulnerabilities.

This two-layer design means that an application encrypting files should use Fernet, while an application that must interoperate with a TLS certificate chain or implement a specific key exchange protocol uses the hazmat layer with full awareness of what it is doing.

## Installing cryptography and Using Fernet

The README gives a single installation command:

```bash
pip install cryptography
```

The PyPI package ships pre-built wheels for common platforms, so most installs do not require a local Rust or C toolchain. Full installation details are in the documentation at cryptography.io/en/latest/installation/.

The README's introductory example shows the complete Fernet workflow:

```python
from cryptography.fernet import Fernet
# Put this somewhere safe!
key = Fernet.generate_key()
f = Fernet(key)
token = f.encrypt(b"A really secret message. Not for prying eyes.")
f.decrypt(token)
```

Fernet.generate_key() produces a URL-safe base64-encoded 32-byte key. The f.encrypt() call returns a token that includes the ciphertext, an HMAC, and a timestamp. f.decrypt() verifies the HMAC before returning the plaintext, so a tampered or expired token raises an exception rather than returning corrupted data. The key must be stored separately from the token; the README comment says to put it somewhere safe.

The package ships documentation at cryptography.io. Security issues have a separate reporting path documented at cryptography.io/en/latest/security/.

## The Rust-OpenSSL Backend and Build Requirements

Earlier versions of cryptography used cffi to bind Python to OpenSSL. Starting in 2021, the project began migrating the OpenSSL bindings to Rust, using the maturin build backend. The Cargo.toml in the repository shows a workspace with eight Rust crates: cryptography-crypto, cryptography-cffi, cryptography-key-parsing, cryptography-openssl, cryptography-x509, cryptography-x509-verification, and two helper crates.

The pyproject.toml declares maturin as the build system and lists cffi as a runtime dependency for CPython (not for PyPy). The Cargo workspace sets a minimum supported Rust version (MSRV) of 1.83.0. Building from source therefore requires both a Rust toolchain at 1.83.0 or later and the OpenSSL development headers. Installing from the pre-built PyPI wheels avoids this entirely on Linux x86-64, Linux aarch64, macOS, and Windows.

The version in pyproject.toml is 51.0.0-dev1, reflecting active development on the next major release. The package has no GitHub releases, so the canonical version source is the PyPI release page.

## X.509 Certificates and Other Capabilities

Beyond Fernet, the package provides a substantial X.509 certificate API. The cryptography-x509 and cryptography-x509-verification Rust crates power parsing, building, and verifying certificate chains. This covers use cases such as inspecting the certificate presented by a TLS server, building a self-signed certificate for testing, or implementing a custom certificate authority workflow.

The hazmat layer exposes asymmetric key types including RSA, DSA, ECDSA, Ed25519, and X25519 for key exchange. Key derivation functions include PBKDF2HMAC, Scrypt, HKDF, and ConcatKDF. Symmetric cipher modes include AES in CBC, GCM, CTR, and several others. Message authentication codes include HMAC and Poly1305.

The README and project description call it a cryptographic standard library, which is accurate in the sense that it covers the algorithms a generalist Python developer is likely to encounter. It does not cover more specialized areas such as post-quantum algorithms or zero-knowledge proof systems.

## Limitations and Cases Where cryptography Is Not Enough

The Fernet recipe is limited to symmetric encryption. When a use case requires asymmetric encryption, digital signatures, or key exchange, the developer must move into the hazmat layer and understand the specific algorithm's requirements. The documentation at cryptography.io covers this, but the package does not provide a second high-level recipe for asymmetric operations.

The package does not provide a TLS implementation. It exposes the cryptographic primitives that TLS uses, but an application that needs to establish TLS connections should use the ssl module from the Python standard library or a higher-level library such as httpx or aiohttp, which handles the TLS handshake and certificate verification.

Building from source on unusual platforms, or on systems where OpenSSL is not installed in a standard location, requires careful configuration of the build environment. The maturin build backend and Rust MSRV add to the list of prerequisites.

## PyCryptodome as an Alternative

PyCryptodome is a self-contained Python cryptography library that maintains its own implementations of cryptographic algorithms in C, without depending on OpenSSL. It is a drop-in replacement for the unmaintained PyCrypto package and exposes a similar API.

The practical difference is in the trust and maintenance model. cryptography delegates its core cryptographic operations to OpenSSL, a heavily audited cryptographic library with decades of security review. Platform vendors and operating system vendors ship OpenSSL, which means it receives security patches as part of regular OS updates. PyCryptodome maintains its own implementations, which are not backed by the same level of external review.

For teams that cannot use packages with a Rust build step or that need to minimize binary dependencies, PyCryptodome is a reasonable alternative. For teams building production infrastructure where OpenSSL is already present and audited in the environment, cryptography's delegation to OpenSSL is an advantage.

## License and Maintenance

The project is dual-licensed under Apache-2.0 OR BSD-3-Clause, as declared in pyproject.toml. Either license can be applied at the user's discretion. Both are permissive and impose no copyleft obligations. The license files are LICENSE, LICENSE.APACHE, and LICENSE.BSD in the repository root.

The last push was on 2026-09-27, indicating active development. The version in pyproject.toml is 51.0.0-dev1, which is a development build of the next release. There are no GitHub releases; historical releases are tracked on PyPI. The repository has a CHANGELOG.rst and a noxfile.py for running the test suite. The .github/workflows/ci.yml file runs continuous integration tests, and the ReadTheDocs badge links to the documentation build status.

## Conclusion

cryptography is the right choice for Python applications that need production-grade cryptographic operations without writing their own OpenSSL bindings. The dual Apache-2.0 / BSD-3-Clause license is permissive and covers both commercial and open-source projects without restriction. Teams should be aware that building from source requires a Rust toolchain at Rust 1.83.0 or later; installing from PyPI wheels avoids that requirement on common platforms. The hazmat layer is not a safe starting point for engineers without a cryptography background; use Fernet or the documented high-level recipes first and consult the hazmat documentation when the recipe layer cannot satisfy the requirement.

## FAQ

### How do I install the cryptography library in Python?

Run pip install cryptography in your terminal. Pre-built wheels are available on PyPI for common platforms, so most installs do not require a Rust or C toolchain. Full installation documentation is at cryptography.io/en/latest/installation/.

### How do I use the cryptography library in Python?

The high-level starting point is the Fernet class in cryptography.fernet. Call Fernet.generate_key() to produce a key, then use Fernet(key).encrypt() to encrypt bytes and Fernet(key).decrypt() to decrypt. For asymmetric keys or other algorithms, the hazmat subpackage exposes lower-level primitives.

### Does cryptography depend on OpenSSL?

Yes. The package uses Rust bindings (via maturin and cffi) to delegate core cryptographic operations to OpenSSL. Building from source requires OpenSSL development headers and a Rust toolchain at version 1.83.0 or later. Installing from PyPI wheels skips the build step on supported platforms.

### What is the difference between the Fernet and hazmat layers?

Fernet is a high-level recipe that handles authenticated symmetric encryption with sensible defaults so callers cannot easily misuse it. The hazmat (hazardous materials) module exposes individual cryptographic primitives for callers who need to implement specific protocols or interoperate with existing systems. The README recommends using the recipe layer unless the hazmat layer is specifically needed.

## Sources

- [Issues](https://github.com/pyca/cryptography/issues)
- [Project website](https://cryptography.io)
- [pyca/cryptography on GitHub](https://github.com/pyca/cryptography)
- [README](https://github.com/pyca/cryptography/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pyca-cryptography
