Cryptographic recipes and primitives for Python developers
—
Comprehensive X.509 certificate handling including certificate creation, parsing, validation, and extension management. Supports certificate signing requests (CSRs), certificate revocation lists (CRLs), and certificate verification workflows.
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtensionOID, SignatureAlgorithmOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsaLoad and parse X.509 certificates from various formats.
def load_pem_x509_certificate(data: bytes) -> Certificate:
"""
Load X.509 certificate from PEM format.
Args:
data (bytes): PEM-encoded certificate data
Returns:
Certificate: Parsed certificate object
"""
def load_der_x509_certificate(data: bytes) -> Certificate:
"""
Load X.509 certificate from DER format.
Args:
data (bytes): DER-encoded certificate data
Returns:
Certificate: Parsed certificate object
"""
def load_pem_x509_certificates(data: bytes) -> List[Certificate]:
"""
Load multiple X.509 certificates from PEM format.
Args:
data (bytes): PEM data containing multiple certificates
Returns:
List[Certificate]: List of parsed certificate objects
"""class Certificate:
def public_key(self):
"""
Get the certificate's public key.
Returns:
PublicKey: The public key (RSA, DSA, EC, Ed25519, Ed448, etc.)
"""
@property
def subject(self) -> Name:
"""Certificate subject name"""
@property
def issuer(self) -> Name:
"""Certificate issuer name"""
@property
def serial_number(self) -> int:
"""Certificate serial number"""
@property
def version(self) -> Version:
"""Certificate version (typically Version.v3)"""
@property
def not_valid_before(self) -> datetime:
"""Certificate validity start time"""
@property
def not_valid_after(self) -> datetime:
"""Certificate validity end time"""
@property
def signature_algorithm_oid(self) -> ObjectIdentifier:
"""OID of signature algorithm used"""
@property
def signature_hash_algorithm(self):
"""Hash algorithm used in signature"""
@property
def extensions(self) -> Extensions:
"""Certificate extensions"""
def fingerprint(self, algorithm) -> bytes:
"""
Calculate certificate fingerprint.
Args:
algorithm: Hash algorithm (e.g., hashes.SHA256())
Returns:
bytes: Certificate fingerprint
"""
def public_bytes(self, encoding: Encoding) -> bytes:
"""
Serialize certificate to bytes.
Args:
encoding: Encoding format (PEM or DER)
Returns:
bytes: Serialized certificate
"""class CertificateBuilder:
def subject_name(self, name: Name) -> 'CertificateBuilder':
"""Set certificate subject name"""
def issuer_name(self, name: Name) -> 'CertificateBuilder':
"""Set certificate issuer name"""
def public_key(self, key) -> 'CertificateBuilder':
"""Set certificate public key"""
def serial_number(self, serial: int) -> 'CertificateBuilder':
"""Set certificate serial number"""
def not_valid_before(self, time: datetime) -> 'CertificateBuilder':
"""Set certificate validity start time"""
def not_valid_after(self, time: datetime) -> 'CertificateBuilder':
"""Set certificate validity end time"""
def add_extension(self, extension, critical: bool) -> 'CertificateBuilder':
"""Add extension to certificate"""
def sign(self, private_key, algorithm) -> Certificate:
"""
Sign and build the certificate.
Args:
private_key: Private key for signing
algorithm: Hash algorithm (e.g., hashes.SHA256())
Returns:
Certificate: Signed certificate
"""
def random_serial_number() -> int:
"""Generate cryptographically secure random serial number"""class Name:
def __init__(self, attributes: List[NameAttribute]):
"""
Create distinguished name from attributes.
Args:
attributes: List of name attributes
"""
def rfc4514_string(self) -> str:
"""RFC 4514 string representation"""
def get_attributes_for_oid(self, oid: ObjectIdentifier) -> List[NameAttribute]:
"""Get all attributes matching an OID"""
class NameAttribute:
def __init__(self, oid: ObjectIdentifier, value: str):
"""
Create name attribute.
Args:
oid: Attribute OID (e.g., NameOID.COMMON_NAME)
value: Attribute value
"""
@property
def oid(self) -> ObjectIdentifier:
"""Attribute OID"""
@property
def value(self) -> str:
"""Attribute value"""
class RelativeDistinguishedName:
def __init__(self, attributes: List[NameAttribute]):
"""Create RDN from attributes"""
def get_attributes_for_oid(self, oid: ObjectIdentifier) -> List[NameAttribute]:
"""Get attributes for specific OID"""class Extensions:
def get_extension_for_oid(self, oid: ObjectIdentifier) -> Extension:
"""Get extension by OID"""
def __iter__(self):
"""Iterate over all extensions"""
class Extension:
@property
def oid(self) -> ObjectIdentifier:
"""Extension OID"""
@property
def critical(self) -> bool:
"""Whether extension is critical"""
@property
def value(self) -> ExtensionType:
"""Extension value object"""
class BasicConstraints:
def __init__(self, ca: bool, path_length: int = None):
"""
Basic constraints extension.
Args:
ca: Whether this is a CA certificate
path_length: Maximum path length for intermediate CAs
"""
@property
def ca(self) -> bool:
"""Is this a CA certificate"""
@property
def path_length(self) -> int:
"""Path length constraint"""
class KeyUsage:
def __init__(self, digital_signature: bool = False, key_agreement: bool = False,
key_cert_sign: bool = False, crl_sign: bool = False,
content_commitment: bool = False, data_encipherment: bool = False,
key_encipherment: bool = False, encipher_only: bool = False,
decipher_only: bool = False):
"""Key usage extension indicating allowed key operations"""
class SubjectAlternativeName:
def __init__(self, general_names: List[GeneralName]):
"""Subject alternative name extension"""
def get_values_for_type(self, type_: type) -> List:
"""Get values for specific general name type"""
class AuthorityKeyIdentifier:
def __init__(self, key_identifier: bytes = None,
authority_cert_issuer: List[GeneralName] = None,
authority_cert_serial_number: int = None):
"""Authority key identifier extension"""
class SubjectKeyIdentifier:
def __init__(self, digest: bytes):
"""Subject key identifier extension"""
@property
def digest(self) -> bytes:
"""Key identifier digest"""class GeneralName:
"""Abstract base for general name types"""
class DNSName:
def __init__(self, value: str):
"""DNS name general name"""
@property
def value(self) -> str:
"""DNS name value"""
class IPAddress:
def __init__(self, value):
"""IP address general name (IPv4/IPv6 address or network)"""
@property
def value(self):
"""IP address value"""
class RFC822Name:
def __init__(self, value: str):
"""Email address general name"""
@property
def value(self) -> str:
"""Email address"""
class UniformResourceIdentifier:
def __init__(self, value: str):
"""URI general name"""
@property
def value(self) -> str:
"""URI value"""
class DirectoryName:
def __init__(self, value: Name):
"""Directory name (distinguished name) general name"""
@property
def value(self) -> Name:
"""Distinguished name"""def load_pem_x509_csr(data: bytes) -> CertificateSigningRequest:
"""Load CSR from PEM format"""
def load_der_x509_csr(data: bytes) -> CertificateSigningRequest:
"""Load CSR from DER format"""
class CertificateSigningRequest:
@property
def subject(self) -> Name:
"""CSR subject name"""
def public_key(self):
"""CSR public key"""
@property
def signature_algorithm_oid(self) -> ObjectIdentifier:
"""Signature algorithm OID"""
@property
def extensions(self) -> Extensions:
"""CSR extension requests"""
def is_signature_valid(self) -> bool:
"""Verify CSR signature"""
class CertificateSigningRequestBuilder:
def subject_name(self, name: Name) -> 'CertificateSigningRequestBuilder':
"""Set CSR subject"""
def add_extension(self, extension, critical: bool) -> 'CertificateSigningRequestBuilder':
"""Add extension request"""
def sign(self, private_key, algorithm) -> CertificateSigningRequest:
"""Sign and build CSR"""def load_pem_x509_crl(data: bytes) -> CertificateRevocationList:
"""Load CRL from PEM format"""
def load_der_x509_crl(data: bytes) -> CertificateRevocationList:
"""Load CRL from DER format"""
class CertificateRevocationList:
@property
def issuer(self) -> Name:
"""CRL issuer"""
@property
def last_update(self) -> datetime:
"""CRL last update time"""
@property
def next_update(self) -> datetime:
"""CRL next update time"""
def get_revoked_certificate_by_serial_number(self, serial: int) -> RevokedCertificate:
"""Get revoked certificate by serial number"""
def __iter__(self):
"""Iterate over revoked certificates"""
class RevokedCertificate:
@property
def serial_number(self) -> int:
"""Serial number of revoked certificate"""
@property
def revocation_date(self) -> datetime:
"""Date certificate was revoked"""
@property
def extensions(self) -> Extensions:
"""Revocation entry extensions"""from cryptography import x509
from cryptography.x509.oid import NameOID
# Load certificate from PEM file
with open('certificate.pem', 'rb') as f:
cert_data = f.read()
cert = x509.load_pem_x509_certificate(cert_data)
# Inspect certificate details
print(f"Subject: {cert.subject.rfc4514_string()}")
print(f"Issuer: {cert.issuer.rfc4514_string()}")
print(f"Serial: {cert.serial_number}")
print(f"Valid from: {cert.not_valid_before}")
print(f"Valid to: {cert.not_valid_after}")
# Extract common name
cn_attributes = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
if cn_attributes:
print(f"Common Name: {cn_attributes[0].value}")
# Check extensions
try:
san_ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
san = san_ext.value
dns_names = san.get_values_for_type(x509.DNSName)
print(f"DNS SANs: {[name.value for name in dns_names]}")
except x509.ExtensionNotFound:
print("No SAN extension found")from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
import datetime
# Generate private key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
# Create certificate
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
x509.NameAttribute(NameOID.COMMON_NAME, "mysite.com"),
])
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365)
).add_extension(
x509.SubjectAlternativeName([
x509.DNSName("mysite.com"),
x509.DNSName("www.mysite.com"),
]),
critical=False,
).add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
).sign(private_key, hashes.SHA256())
# Save certificate and key
cert_pem = cert.public_bytes(Encoding.PEM)
key_pem = private_key.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.PKCS8,
encryption_algorithm=NoEncryption()
)
with open('certificate.pem', 'wb') as f:
f.write(cert_pem)
with open('private_key.pem', 'wb') as f:
f.write(key_pem)from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
# Generate private key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
# Create CSR
subject = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
x509.NameAttribute(NameOID.COMMON_NAME, "example.com"),
])
csr = x509.CertificateSigningRequestBuilder().subject_name(
subject
).add_extension(
x509.SubjectAlternativeName([
x509.DNSName("example.com"),
x509.DNSName("www.example.com"),
]),
critical=False,
).sign(private_key, hashes.SHA256())
# Save CSR
csr_pem = csr.public_bytes(Encoding.PEM)
with open('csr.pem', 'wb') as f:
f.write(csr_pem)
# Verify CSR signature
print(f"CSR signature valid: {csr.is_signature_valid()}")from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
def verify_certificate_chain(cert_chain):
"""Verify a certificate chain"""
for i in range(len(cert_chain) - 1):
cert = cert_chain[i]
issuer_cert = cert_chain[i + 1]
# Verify issuer name matches subject
if cert.issuer != issuer_cert.subject:
return False, f"Issuer name mismatch at position {i}"
# Verify signature
try:
issuer_public_key = issuer_cert.public_key()
issuer_public_key.verify(
cert.signature,
cert.tbs_certificate_bytes,
padding.PKCS1v15(),
cert.signature_hash_algorithm
)
except Exception as e:
return False, f"Signature verification failed at position {i}: {e}"
return True, "Chain verification successful"
# Usage
with open('cert_chain.pem', 'rb') as f:
chain_data = f.read()
cert_chain = x509.load_pem_x509_certificates(chain_data)
valid, message = verify_certificate_chain(cert_chain)
print(f"Chain valid: {valid}, Message: {message}")Common object identifiers for names and extensions:
class NameOID:
COMMON_NAME: ObjectIdentifier
COUNTRY_NAME: ObjectIdentifier
LOCALITY_NAME: ObjectIdentifier
STATE_OR_PROVINCE_NAME: ObjectIdentifier
ORGANIZATION_NAME: ObjectIdentifier
ORGANIZATIONAL_UNIT_NAME: ObjectIdentifier
EMAIL_ADDRESS: ObjectIdentifier
class ExtensionOID:
BASIC_CONSTRAINTS: ObjectIdentifier
KEY_USAGE: ObjectIdentifier
EXTENDED_KEY_USAGE: ObjectIdentifier
SUBJECT_ALTERNATIVE_NAME: ObjectIdentifier
ISSUER_ALTERNATIVE_NAME: ObjectIdentifier
SUBJECT_KEY_IDENTIFIER: ObjectIdentifier
AUTHORITY_KEY_IDENTIFIER: ObjectIdentifier
CRL_DISTRIBUTION_POINTS: ObjectIdentifier
AUTHORITY_INFORMATION_ACCESS: ObjectIdentifier
CERTIFICATE_POLICIES: ObjectIdentifier
class SignatureAlgorithmOID:
RSA_WITH_SHA256: ObjectIdentifier
RSA_WITH_SHA384: ObjectIdentifier
RSA_WITH_SHA512: ObjectIdentifier
ECDSA_WITH_SHA256: ObjectIdentifier
ECDSA_WITH_SHA384: ObjectIdentifier
ECDSA_WITH_SHA512: ObjectIdentifierclass InvalidVersion(ValueError):
"""Invalid certificate version"""
class ExtensionNotFound(ValueError):
"""Certificate extension not found"""
class DuplicateExtension(ValueError):
"""Duplicate extension in certificate"""
class UnsupportedGeneralNameType(ValueError):
"""Unsupported general name type"""Install with Tessl CLI
npx tessl i tessl/pypi-cryptography