Registry / auth-security / pyhanko-certvalidator

pyhanko-certvalidator

JSON →
library0.31.4pypypi✓ verified 27d ago

pyhanko-certvalidator is a Python library designed for robust validation of X.509 certificates and certificate paths. Originally forked from wbond/certvalidator, it has since diverged significantly, incorporating features and architectural changes tailored for the pyHanko ecosystem, particularly for PDF digital signature validation. It supports advanced features such as revocation checks (CRLs and OCSP), point-in-time validation, policy constraints, and various signature algorithms. The current version is 0.30.2, and it follows a regular release cadence as part of the broader pyHanko project.

pip install pyhanko-certvalidator
INSTALL
IMPORT
SIG · PYHANKO-CERTVALIDA
P
pyhanko-certvalidator
auth-securitypythonv0.31.4
Install
3.3s avg
Import
720ms
Disk
41MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.9–3.13
musl
3.9–3.13
Install & Compatibility
Where this runs
tested against v0.31.4 · 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
musl
py 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.758s · 42MB
glibc
py 3.10–3.95 runs
installs and imports cleanly · install 3.3s · import 0.682s · 42MB
41MB installed
● package 41MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

ValidationContext
✓ from pyhanko_certvalidator import ValidationContext
CertificateValidator
✓ from pyhanko_certvalidator import CertificateValidator
errors
✓ from pyhanko_certvalidator import errors
ValidationContext
✓
✗ from certvalidator import ValidationContext
The package was renamed from 'certvalidator' to 'pyhanko_certvalidator' to avoid namespace conflicts.

This example demonstrates how to perform a basic certificate path validation using `pyhanko-certvalidator`. It generates a synthetic certificate chain (Root CA -> Intermediate CA -> End-Entity) and then uses `ValidationContext` and `CertificateValidator` to verify the end-entity certificate against the trusted root. It showcases the asynchronous `async_validate_tls` method, which is the recommended approach for modern usage.

import asyncio from datetime import datetime, timedelta from asn1crypto import x509, pem from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend from pyhanko_certvalidator import ValidationContext, CertificateValidator, errors async def run_validation_example(): # 1. Generate a self-signed root CA certificate root_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) root_subject = x509.Name([ x509.NameAttribute('2.5.4.6', 'US'), x509.NameAttribute('2.5.4.10', 'Root CA Inc.'), x509.NameAttribute('2.5.4.3', 'Example Root CA') ]) root_cert = x509.CertificateBuilder(). subject_name(root_subject). issuer_name(root_subject). public_key(root_key.public_key()). serial_number(x509.random_serial_number()). not_valid_before(datetime.utcnow() - timedelta(days=1)). not_valid_after(datetime.utcnow() + timedelta(days=3650)). add_extension(x509.BasicConstraints(ca=True, path_length=1), critical=True). add_extension(x509.KeyUsage(key_cert_sign=True, crl_sign=True), critical=True). sign(root_key, hashes.SHA256(), default_backend()) # 2. Generate an intermediate CA certificate signed by the root CA intermediate_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) intermediate_subject = x509.Name([ x509.NameAttribute('2.5.4.6', 'US'), x509.NameAttribute('2.5.4.10', 'Intermediate CA Corp.'), x509.NameAttribute('2.5.4.3', 'Example Intermediate CA') ]) intermediate_cert = x509.CertificateBuilder(). subject_name(intermediate_subject). issuer_name(root_subject). public_key(intermediate_key.public_key()). serial_number(x509.random_serial_number()). not_valid_before(datetime.utcnow() - timedelta(days=1)). not_valid_after(datetime.utcnow() + timedelta(days=1825)). add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True). add_extension(x509.KeyUsage(key_cert_sign=True, crl_sign=True), critical=True). sign(root_key, hashes.SHA256(), default_backend()) # 3. Generate an end-entity certificate signed by the intermediate CA ee_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) ee_subject = x509.Name([ x509.NameAttribute('2.5.4.6', 'US'), x509.NameAttribute('2.5.4.10', 'End-Entity Dept.'), x509.NameAttribute('2.5.4.3', 'example.com') ]) ee_cert = x509.CertificateBuilder(). subject_name(ee_subject). issuer_name(intermediate_subject). public_key(ee_key.public_key()). serial_number(x509.random_serial_number()). not_valid_before(datetime.utcnow() - timedelta(days=1)). not_valid_after(datetime.utcnow() + timedelta(days=365)). add_extension(x509.BasicConstraints(ca=False), critical=True). add_extension(x509.KeyUsage(digital_signature=True, key_encipherment=True), critical=True). add_extension(x509.ExtendedKeyUsage([x509.ExtendedKeyUsageOID.SERVER_AUTH]), critical=False). sign(intermediate_key, hashes.SHA256(), default_backend()) # Prepare certificates for validation root_cert_asn1 = x509.Certificate.load(root_cert.public_bytes(encoding=serialization.Encoding.DER)) intermediate_cert_asn1 = x509.Certificate.load(intermediate_cert.public_bytes(encoding=serialization.Encoding.DER)) ee_cert_asn1 = x509.Certificate.load(ee_cert.public_bytes(encoding=serialization.Encoding.DER)) # 4. Create a ValidationContext with the root CA as trust anchor validation_context = ValidationContext( trust_roots=[root_cert_asn1], # For more complex scenarios, you might add 'intermediate_certs' or enable fetching: intermediate_certs=[intermediate_cert_asn1], allow_fetching=False # Set to True to allow HTTP fetching of CRLs/OCSP ) # 5. Validate the end-entity certificate path try: validator = CertificateValidator(ee_cert_asn1, [], validation_context) valid_paths = await validator.async_validate_tls() print(f"Certificate for {ee_cert_asn1.subject.human_friendly} is VALID.") print("Validated paths found:") for path in valid_paths: print(f" - {len(path.certs)} certificates in path") except errors.PathValidationError as e: print(f"Certificate validation FAILED: {e}") except Exception as e: print(f"An unexpected error occurred: {e}") if __name__ == '__main__': asyncio.run(run_validation_example())
Debug
Known issues
breakingStarting with version 0.17.0, the library underwent significant refactoring to favour asynchronous I/O. While most high-level API entrypoints can still be used synchronously, their `asyncio` equivalents are now preferred and many synchronous methods have been deprecated.
fix
Migrate your code to use the `asyncio` variants of API calls (e.g., `async_validate_tls()` instead of `validate_tls()`). If using custom fetchers, consider implementing `aiohttp`-based fetchers for better performance in async contexts, though `requests`-based ones remain the default.
affects: >=0.17.0
breakingThe package was renamed from `certvalidator` to `pyhanko_certvalidator` to prevent namespace collisions, as the library significantly diverged from the original `wbond/certvalidator` project.
fix
Update all `import certvalidator` statements to `import pyhanko_certvalidator` or `from pyhanko_certvalidator import ...`.
affects: <=0.12.0 (when the fork occurred, though actual package rename for public consumption happened later for older versions)
gotchaThis library is a fork of `wbond/certvalidator` and has diverged considerably. While basic usage might be similar, specific features, internal workings, and advanced configurations may differ. Directly swapping between the two without review may lead to unexpected behaviour.
fix
Thoroughly review the `pyhanko-certvalidator` documentation, especially if migrating from `wbond/certvalidator`, to understand any API or behaviour differences.
affects: All versions
gotchaGitHub issues are disabled on the `pyhanko-certvalidator` repository. Bug reports and usage questions should be submitted to the main `pyHanko` issue tracker or discussion forum.
fix
For support or bug reporting, refer to the main pyHanko project's GitHub issues page: `https://github.com/MatthiasValvekens/pyHanko/issues`.
affects: All versions
gotchaWhile `pyhanko-certvalidator` previously supported Python 3.7+, the broader `pyHanko` project and its latest releases now require Python 3.10 or later for full compatibility and intended functionality.
fix
Ensure your Python environment is running Python 3.10 or newer.
affects: 0.30.0 onwards (recommendation for ecosystem)
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'certvalidator'
The `pyhanko-certvalidator` library is a fork of `certvalidator`, and its modules are installed under the `pyhanko_certvalidator` namespace, meaning direct imports from `certvalidator` will fail.
fix
Update import statements to use `pyhanko_certvalidator` instead of `certvalidator`, for example, `from pyhanko_certvalidator import ValidationContext`.
The signer's certificate could not be validated.
This generic error, often stemming from `pyhanko_certvalidator.errors.PathValidationError` or `pyhanko_certvalidator.errors.RevokedError`, indicates that a complete and trusted path from the signer's certificate to a configured trust anchor could not be built, or the certificate was found to be revoked.
fix
Ensure that all necessary intermediate certificates are supplied to the `ValidationContext`, that the root certificate is explicitly designated as a trust anchor, and that valid and accessible revocation information (CRLs or OCSP responses) is available.
ValueError: Expected PEM data when extracting certs from application/x-x509-ca-cert payload.
The certificate fetching or parsing mechanism received data that was expected to be PEM-encoded (Privacy-Enhanced Mail) but was in a different format (e.g., DER - Distinguished Encoding Rules) or the `Content-Type` header from the server was misleading.
fix
Verify the encoding of the certificate data being provided. If fetching from a URL, ensure the server correctly provides PEM-encoded data or manually convert the data to PEM format before passing it to the library.
KeyError: 'content-type'
During an attempt to fetch a certificate, CRL, or OCSP response from a remote URL, the server's HTTP response did not include a 'Content-Type' header, which the `pyhanko-certvalidator` fetcher expects for proper processing.
fix
This issue typically requires the remote server to be configured to send the `Content-Type` header. If server-side changes are not possible, consider manually fetching the resource and providing its content to the validator, or implement a custom fetcher that can handle missing `Content-Type` headers.
Upgrade
Version history
0.31.4latest on PyPI · released Jul 25, 2026
Audit
Dependencies
asn1cryptorequiredCore cryptographic operations for ASN.1 parsing and serialization.
cryptographyrequiredFundamental cryptographic primitives and algorithms.
uritoolsrequiredRFC 3986 compliant URI parsing.
oscryptorequiredAccess to system trust stores and some cryptographic operations.
requestsrequiredDefault HTTP client for fetching CRLs/OCSP responses.
aiohttpoptionalAlternative, more performant asynchronous HTTP client for fetching CRLs/OCSP responses. Requires explicit configuration.
Agent activity
11 hits · last 30 days
node
8
OpenAI (training)
1
Resources
pyhanko-certvalidator — pip install pyhanko-certvalidator · libregistry