{"library":"pyhanko-certvalidator","title":"pyhanko-certvalidator","description":"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.","language":"python","status":"active","last_verified":"Thu May 14","install":{"commands":["pip install pyhanko-certvalidator"],"cli":null},"imports":["from pyhanko_certvalidator import ValidationContext","from pyhanko_certvalidator import CertificateValidator","from pyhanko_certvalidator import errors"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import asyncio\nfrom datetime import datetime, timedelta\n\nfrom asn1crypto import x509, pem\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.backends import default_backend\nfrom pyhanko_certvalidator import ValidationContext, CertificateValidator, errors\n\nasync def run_validation_example():\n    # 1. Generate a self-signed root CA certificate\n    root_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())\n    root_subject = x509.Name([\n        x509.NameAttribute('2.5.4.6', 'US'),\n        x509.NameAttribute('2.5.4.10', 'Root CA Inc.'),\n        x509.NameAttribute('2.5.4.3', 'Example Root CA')\n    ])\n    root_cert = x509.CertificateBuilder().\n        subject_name(root_subject).\n        issuer_name(root_subject).\n        public_key(root_key.public_key()).\n        serial_number(x509.random_serial_number()).\n        not_valid_before(datetime.utcnow() - timedelta(days=1)).\n        not_valid_after(datetime.utcnow() + timedelta(days=3650)).\n        add_extension(x509.BasicConstraints(ca=True, path_length=1), critical=True).\n        add_extension(x509.KeyUsage(key_cert_sign=True, crl_sign=True), critical=True).\n        sign(root_key, hashes.SHA256(), default_backend())\n\n    # 2. Generate an intermediate CA certificate signed by the root CA\n    intermediate_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())\n    intermediate_subject = x509.Name([\n        x509.NameAttribute('2.5.4.6', 'US'),\n        x509.NameAttribute('2.5.4.10', 'Intermediate CA Corp.'),\n        x509.NameAttribute('2.5.4.3', 'Example Intermediate CA')\n    ])\n    intermediate_cert = x509.CertificateBuilder().\n        subject_name(intermediate_subject).\n        issuer_name(root_subject).\n        public_key(intermediate_key.public_key()).\n        serial_number(x509.random_serial_number()).\n        not_valid_before(datetime.utcnow() - timedelta(days=1)).\n        not_valid_after(datetime.utcnow() + timedelta(days=1825)).\n        add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True).\n        add_extension(x509.KeyUsage(key_cert_sign=True, crl_sign=True), critical=True).\n        sign(root_key, hashes.SHA256(), default_backend())\n\n    # 3. Generate an end-entity certificate signed by the intermediate CA\n    ee_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())\n    ee_subject = x509.Name([\n        x509.NameAttribute('2.5.4.6', 'US'),\n        x509.NameAttribute('2.5.4.10', 'End-Entity Dept.'),\n        x509.NameAttribute('2.5.4.3', 'example.com')\n    ])\n    ee_cert = x509.CertificateBuilder().\n        subject_name(ee_subject).\n        issuer_name(intermediate_subject).\n        public_key(ee_key.public_key()).\n        serial_number(x509.random_serial_number()).\n        not_valid_before(datetime.utcnow() - timedelta(days=1)).\n        not_valid_after(datetime.utcnow() + timedelta(days=365)).\n        add_extension(x509.BasicConstraints(ca=False), critical=True).\n        add_extension(x509.KeyUsage(digital_signature=True, key_encipherment=True), critical=True).\n        add_extension(x509.ExtendedKeyUsage([x509.ExtendedKeyUsageOID.SERVER_AUTH]), critical=False).\n        sign(intermediate_key, hashes.SHA256(), default_backend())\n\n    # Prepare certificates for validation\n    root_cert_asn1 = x509.Certificate.load(root_cert.public_bytes(encoding=serialization.Encoding.DER))\n    intermediate_cert_asn1 = x509.Certificate.load(intermediate_cert.public_bytes(encoding=serialization.Encoding.DER))\n    ee_cert_asn1 = x509.Certificate.load(ee_cert.public_bytes(encoding=serialization.Encoding.DER))\n\n    # 4. Create a ValidationContext with the root CA as trust anchor\n    validation_context = ValidationContext(\n        trust_roots=[root_cert_asn1],\n        # For more complex scenarios, you might add 'intermediate_certs' or enable fetching:\n        intermediate_certs=[intermediate_cert_asn1],\n        allow_fetching=False # Set to True to allow HTTP fetching of CRLs/OCSP\n    )\n\n    # 5. Validate the end-entity certificate path\n    try:\n        validator = CertificateValidator(ee_cert_asn1, [], validation_context)\n        valid_paths = await validator.async_validate_tls()\n        print(f\"Certificate for {ee_cert_asn1.subject.human_friendly} is VALID.\")\n        print(\"Validated paths found:\")\n        for path in valid_paths:\n            print(f\" - {len(path.certs)} certificates in path\")\n    except errors.PathValidationError as e:\n        print(f\"Certificate validation FAILED: {e}\")\n    except Exception as e:\n        print(f\"An unexpected error occurred: {e}\")\n\nif __name__ == '__main__':\n    asyncio.run(run_validation_example())\n","lang":"python","description":"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.","tag":null,"tag_description":null,"last_tested":"2026-04-25","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]},"compatibility":{"tag":null,"tag_description":null,"last_tested":"2026-05-14","installed_version":"0.29.1","pypi_latest":"0.31.1","is_stale":true,"summary":{"python_range":"3.10–3.9","success_rate":100,"avg_install_s":3.2,"avg_import_s":1.01,"wheel_type":"wheel"},"results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.83,"mem_mb":15.9,"disk_size":"41.5M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.9,"mem_mb":15.8,"disk_size":"41.2M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":3.3,"import_time_s":0.57,"mem_mb":15.9,"disk_size":"42M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.6,"mem_mb":15.8,"disk_size":"42M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":1.09,"mem_mb":18.1,"disk_size":"44.5M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":1.37,"mem_mb":17.9,"disk_size":"44.2M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":3.1,"import_time_s":0.93,"mem_mb":18.1,"disk_size":"45M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.97,"mem_mb":17.9,"disk_size":"45M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":1.19,"mem_mb":19.5,"disk_size":"36.0M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":1.39,"mem_mb":19.4,"disk_size":"35.8M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":2.8,"import_time_s":1.18,"mem_mb":19.5,"disk_size":"36M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":1.34,"mem_mb":19.4,"disk_size":"36M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":1.16,"mem_mb":20.5,"disk_size":"35.8M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":1.38,"mem_mb":20.4,"disk_size":"35.4M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":2.8,"import_time_s":1.1,"mem_mb":20.5,"disk_size":"36M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":1.29,"mem_mb":20.4,"disk_size":"36M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":null,"import_time_s":0.74,"mem_mb":15.6,"disk_size":"41.5M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.8,"mem_mb":15.5,"disk_size":"41.3M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":"clean","install_time_s":3.9,"import_time_s":0.67,"mem_mb":15.6,"disk_size":"42M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"pyhanko-certvalidator","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.77,"mem_mb":15.5,"disk_size":"42M"}]}}