x509
March 22, 2026 · View on GitHub
The pyeudiw.x509 module provides trust evaluation using X.509 PKI according to RFC 5280.
Installation
pip install pyeudiw
Verifying an x5c Certificate Chain
Verify a chain of x509 certificates (e.g. from a JWT x5c header):
from pyeudiw.x509.verify import verify_x509_attestation_chain
from pyeudiw.x509.crl_helper import CRLHelper
# x5c: list of base64url-encoded DER certificates
x5c = [b"...", b"...", b"..."] # bytes, or PEM strings
# Without CRL check
is_valid = verify_x509_attestation_chain(x5c)
# With CRL revocation check
crls = [
CRLHelper.from_url("https://crl.example.com/ca.crl"),
]
is_valid = verify_x509_attestation_chain(x5c, crls=crls)
Certificate Format Conversions
from pyeudiw.x509.verify import (
B64DER_cert_to_DER_cert,
B64DER_cert_to_PEM_cert,
DER_cert_to_B64DER_cert,
PEM_cert_to_B64DER_cert,
PEM_cert_to_DER_cert,
to_DER_cert,
to_PEM_cert,
pem_to_pems_list,
to_pem_list,
to_der_list,
)
# Base64 DER <-> DER
der = B64DER_cert_to_DER_cert(base64_der_string)
b64 = DER_cert_to_B64DER_cert(der_bytes)
# PEM <-> DER
pem_str = to_PEM_cert(cert) # cert can be str or bytes
der_bytes = to_DER_cert(cert)
# Split concatenated PEM into list
pem_list = pem_to_pems_list("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\n...")
# Convert lists
pem_list = to_pem_list(der_list)
der_list = to_der_list(pem_list)
Extracting Information from x5c
from pyeudiw.x509.verify import (
get_issuer_from_x5c,
get_trust_anchor_from_x5c,
get_expiry_date_from_x5c,
get_public_key_from_x5c,
get_x509_info,
)
x5c = [b"...", b"..."] # Chain: leaf first, root last
issuer = get_issuer_from_x5c(x5c)
trust_anchor = get_trust_anchor_from_x5c(x5c)
expiry = get_expiry_date_from_x5c(x5c)
# Public key from leaf certificate (for JWT verification)
pubkey = get_public_key_from_x5c(x5c)
# Human-readable cert info
info = get_x509_info(x5c[0], san_dns=True)
CRL Helper
from pyeudiw.x509.crl_helper import CRLHelper
# From URL
crl = CRLHelper.from_url("https://crl.example.com/ca.crl")
# From PEM/DER string or bytes
crl = CRLHelper.from_crl(crl_pem_or_der, uri="https://crl.example.com/ca.crl")
# Check if a certificate is revoked (serial_number: int or hex string)
is_revoked = crl.is_revoked(serial_number=12345)
Building x5c-Like Chains
ChainBuilder creates a certificate chain (root CA → intermediate(s) → leaf). Call gen_certificate in that order: root first, leaf last. The resulting chain from get_chain() is returned as leaf first (suitable for x5c).
from cryptography import x509
from pyeudiw.x509.chain_builder import ChainBuilder
chain = ChainBuilder()
# 1. Root CA (add first)
chain.gen_certificate(
cn="ca.example.com",
organization_name="Example CA",
country_name="IT",
email_address="info@ca.example.com",
dns="ca.example.com",
uri="https://ca.example.com",
ca=True,
path_length=1,
crl_distr_point="https://ca.example.com/crl.pem",
)
# 2. Intermediate CA
chain.gen_certificate(
cn="intermediate.example.com",
organization_name="Example Intermediate",
country_name="IT",
email_address="info@intermediate.example.com",
dns="intermediate.example.com",
uri="https://intermediate.example.com",
ca=True,
path_length=0,
)
# 3. Leaf (end-entity) certificate
chain.gen_certificate(
cn="leaf.example.com",
organization_name="Example Leaf",
country_name="IT",
email_address="info@leaf.example.com",
dns="leaf.example.com",
uri="https://leaf.example.com/openid4vp",
ca=False,
path_length=None,
private_key=my_private_key, # optional; generates EC P-256 if omitted
)
# Get chain as DER (for x5c) or PEM
der_chain = chain.get_chain("DER") # list[bytes], leaf first
pem_chain = chain.get_chain("PEM") # list[str]
ca_cert = chain.get_ca("DER") # root CA only
gen_certificate parameters
| Parameter | Required | Description |
|---|---|---|
cn | ✓ | Common Name |
organization_name | ✓ | Organization |
country_name | ✓ | Country code |
email_address | ✓ | |
dns | ✓ | DNS name (SubjectAlternativeName) |
uri | ✓ | URI (SubjectAlternativeName) |
ca | ✓ | True for CA, False for leaf |
path_length | ✓ | int for CA (max depth), None for leaf |
private_key | EC or RSA key; auto-generated if omitted | |
crl_distr_point | CRL distribution URL | |
not_valid_before / not_valid_after | Validity period (default: ±1 day / +1 year) |
Using with JWK
To use x5c keys for JWT verification:
from pyeudiw.jwk.parse import parse_x5c_keys
from pyeudiw.x509.verify import verify_x509_attestation_chain
from pyeudiw.jwt.jws_helper import JWSHelper
# 1. Verify the chain
x5c_bytes = [cert.encode() if isinstance(cert, str) else cert for cert in x5c]
if not verify_x509_attestation_chain(x5c_bytes):
raise ValueError("Invalid certificate chain")
# 2. Extract keys
keys = parse_x5c_keys(x5c)
verifier = JWSHelper(jwks=[keys[0].as_dict()])
payload = verifier.verify(jws)