Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Certificate

Certificate represents an RFC 5280 X.509 v3 certificate. Parsing is lazy: from_der performs only a 4-operation shallow scan (outer SEQUENCE TLV + TBSCertificate SEQUENCE TLV); the full RFC 5280 decode is deferred to the first field access, after which every property is cached in an OnceLock and returned in O(1) on subsequent calls.

Construction

Certificate.from_der(data: bytes) -> Certificate

Parse a DER-encoded X.509 certificate. Performs a shallow 4-operation structural scan on construction; the full decode is deferred to the first field access.

Certificate.full_from_der(data: bytes) -> Certificate

Parse and perform a complete RFC 5280 decode immediately. All field getters are on the warm path from the first call. Use when benchmarking full parse cost or when latency on the first field access must be avoided.

Certificate.from_pem(data: bytes) -> Certificate | list[Certificate]

Parse a PEM-encoded certificate. Returns a single Certificate for a single PEM block, or a list[Certificate] when multiple blocks are present.

Certificate.to_pem(cert_or_list) -> bytes

Encode one or more Certificate objects back to PEM.

Certificate.from_pyca(pyca_cert: object) -> Certificate

Convert a cryptography.x509.Certificate to a synta.Certificate by serialising to DER and calling from_der. Raises ImportError if the cryptography package is not installed.

cert.to_pyca() -> cryptography.x509.Certificate

Return a cryptography.x509.Certificate backed by the same DER bytes (no re-encoding). Use for cryptographic operations not directly supported by synta. Raises ImportError if the cryptography package is not installed.

Properties

All properties are cached after first access. to_der skips the lock entirely (direct reference to the stored bytes, no copy).

Identity

PropertyTypeDescription
serial_numberintArbitrary-precision Python int (RFC 5280: up to 20 bytes)
versionint | NoneVersion field: None = v1, 1 = v2, 2 = v3
issuerstrRFC 4514 DN string
issuer_raw_derbytesRaw DER of issuer Name SEQUENCE
subjectstrRFC 4514 DN string
subject_raw_derbytesRaw DER of subject Name SEQUENCE

Validity

PropertyTypeDescription
not_beforestr"YYMMDDHHMMSSZ" (UTCTime) or "YYYYMMDDHHMMSSZ" (GeneralizedTime)
not_afterstrSame format as not_before

Signature

PropertyTypeDescription
signature_algorithmstrHuman-readable algorithm name (e.g. "RSA", "ECDSA", "ML-DSA-65") or dotted OID for unrecognised algorithms
signature_algorithm_oidObjectIdentifierAlways machine-readable dotted OID
signature_algorithm_paramsbytes | NoneDER-encoded parameters, or None when absent (e.g. Ed25519)
signature_valuebytesRaw signature bytes (BIT STRING value, unused-bit prefix stripped)

Subject Public Key

PropertyTypeDescription
public_key_algorithmstrHuman-readable name or dotted OID
public_key_algorithm_oidObjectIdentifierDotted OID (e.g. "1.2.840.10045.2.1" for id-ecPublicKey)
public_key_algorithm_paramsbytes | NoneDER parameters (curve OID for EC; None for RSA/EdDSA)
public_keybytesRaw SubjectPublicKeyInfo BIT STRING content (unused-bit byte stripped)
subject_public_key_info_raw_derbytesFull SubjectPublicKeyInfo SEQUENCE DER (for SKI/AKI computation)

Raw DER Spans

Property / MethodTypeDescription
to_der()bytesComplete original certificate DER (zero-copy)
tbs_bytesbytesTBSCertificate DER — the bytes that were signed
issuer_raw_derbytesIssuer Name SEQUENCE DER
subject_raw_derbytesSubject Name SEQUENCE DER
extensions_derbytes | NoneSEQUENCE OF Extension DER, or None for v1/v2

Methods

MethodSignatureReturnsDescription
to_der()()bytesOriginal DER bytes (zero-copy)
get_extension_value_der(oid: str | ObjectIdentifier)bytes | NoneReturn the extnValue content bytes for the named extension OID, or None if absent. Scans in O(n) over the already-parsed Extension list. Raises ValueError for invalid OID strings.
subject_alt_names()()list[tuple[int, bytes]]SAN entries as (tag_number, content) pairs. Returns [] when no SAN extension is present. Use synta.general_name constants to dispatch on tag_number.
general_names(oid: str | ObjectIdentifier)list[tuple[int, bytes]]Decode any extension whose value is SEQUENCE OF GeneralName.
certificate_policies()()list[PolicyInformation]Decode the CertificatePolicies extension (OID 2.5.29.32). Returns [] when absent.
verify_issued_by(issuer: Certificate)NoneVerify this certificate was signed by issuer. Raises ValueError on failure.
fingerprint(algorithm: str)bytesCertificate fingerprint using the named hash (e.g. "sha256"). Raises ValueError for unknown algorithms.

Full class stub

class Certificate:
    @staticmethod
    def from_der(data: bytes) -> Certificate: ...
    @staticmethod
    def full_from_der(data: bytes) -> Certificate: ...
    @staticmethod
    def from_pyca(pyca_cert: object) -> Certificate: ...
    def to_pyca(self) -> object: ...
    def __repr__(self) -> str: ...

    # Identity
    serial_number: int
    version: int | None
    issuer: str
    issuer_raw_der: bytes
    subject: str
    subject_raw_der: bytes

    # Validity
    not_before: str
    not_after: str

    # Signature
    signature_algorithm: str
    signature_algorithm_oid: ObjectIdentifier
    signature_algorithm_params: bytes | None
    signature_value: bytes

    # Subject public key
    public_key_algorithm: str
    public_key_algorithm_oid: ObjectIdentifier
    public_key_algorithm_params: bytes | None
    public_key: bytes
    subject_public_key_info_raw_der: bytes

    # Raw DER spans
    def to_der(self) -> bytes: ...
    tbs_bytes: bytes
    extensions_der: bytes | None

    # Methods
    def get_extension_value_der(self, oid: str) -> bytes | None: ...
    def subject_alt_names(self) -> list[tuple[int, bytes]]: ...
    def verify_issued_by(self, issuer: Certificate) -> None: ...
    def fingerprint(self, algorithm: str) -> bytes: ...

Usage

import synta

# Parse a DER-encoded certificate
with open('cert.der', 'rb') as f:
    cert = synta.Certificate.from_der(f.read())

# Identity fields
print(cert.subject)            # RFC 4514-style DN string
print(cert.issuer)             # RFC 4514-style DN string
print(cert.serial_number)      # Python int (arbitrary precision)
print(cert.version)            # None (v1), 1 (v2), or 2 (v3)

# Validity
print(cert.not_before)         # String, e.g. "230101000000Z"
print(cert.not_after)          # String, e.g. "320101000000Z"

# Signature algorithm
print(cert.signature_algorithm)      # Human-readable name or dotted OID
print(cert.signature_algorithm_oid)  # Dotted OID string (always machine-readable)
print(cert.signature_algorithm_params)  # DER bytes or None (e.g. None for Ed25519)
print(cert.signature_value)          # Raw signature bytes

# Subject public key
print(cert.public_key_algorithm)       # Human-readable name or dotted OID
print(cert.public_key_algorithm_oid)   # Dotted OID string
print(cert.public_key_algorithm_params)  # DER bytes or None (EC: curve OID)
print(cert.public_key)                 # Raw SubjectPublicKeyInfo bit-string bytes

# Raw DER spans
print(cert.to_der())        # Complete original certificate DER bytes
print(cert.tbs_bytes)       # TBSCertificate DER (the bytes that were signed)
print(cert.issuer_raw_der)  # Issuer Name SEQUENCE DER
print(cert.subject_raw_der) # Subject Name SEQUENCE DER
print(cert.extensions_der)  # Extensions SEQUENCE OF DER, or None for v1/v2

# Look up a single extension by OID — returns the extnValue OCTET STRING
# content (the DER-encoded extension-specific structure), or None if absent.
san_der = cert.get_extension_value_der("2.5.29.17")   # SubjectAltName
bc_der  = cert.get_extension_value_der("2.5.29.19")   # BasicConstraints
eku_der = cert.get_extension_value_der("2.5.29.37")   # ExtendedKeyUsage

SAN access

import ipaddress
import synta.general_name as gn

# High-level: subject_alt_names() combines extension lookup and GeneralName parsing
for tag_num, content in cert.subject_alt_names():
    if tag_num == gn.DNS_NAME:
        print("dns:",   content.decode("ascii"))
    elif tag_num == gn.IP_ADDRESS:
        print("ip:",    ipaddress.ip_address(content))
    elif tag_num == gn.RFC822_NAME:
        print("email:", content.decode("ascii"))
    elif tag_num == gn.URI:
        print("uri:",   content.decode("ascii"))
    elif tag_num == gn.DIRECTORY_NAME:
        attrs = synta.parse_name_attrs(content)   # [(oid_str, value_str), …]
        print("dirname:", attrs)

Iterating extensions manually

# Iterate all extensions when you need every extension
ext_der = cert.extensions_der
if ext_der:
    dec = synta.Decoder(ext_der, synta.Encoding.DER)
    exts = dec.decode_sequence()
    while not exts.is_empty():
        ext_tlv = exts.decode_raw_tlv()  # bytes covering one Extension TLV

Signature verification

# Verify a certificate was signed by a CA certificate
ca_cert = synta.Certificate.from_der(open("ca.der", "rb").read())
end_cert = synta.Certificate.from_der(open("end.der", "rb").read())
try:
    end_cert.verify_issued_by(ca_cert)
    print("Signature valid")
except ValueError as e:
    print(f"Invalid: {e}")

See also: