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

PKCS Loaders

Five top-level functions extract X.509 certificates and/or PKCS#8 private keys from container formats (PKCS#7, PKCS#12). All functions accept DER, BER, or CER input; the encoding is detected automatically.

Functions

load_der_pkcs7_certificates

load_der_pkcs7_certificates(data: bytes) -> list[Certificate]

Parse a DER or BER PKCS#7 SignedData blob and return its embedded certificates. BER indefinite-length encodings (common in PKCS#7 files from older tools) are handled transparently. Non-certificate entries in the CertificateSet are silently skipped.

Raises ValueError if the outer ContentInfo contentType is not id-signedData, or on any ASN.1 structural error.

load_pem_pkcs7_certificates

load_pem_pkcs7_certificates(data: bytes) -> list[Certificate]

Decode PEM block(s) in data (any label accepted: PKCS7, CMS, CERTIFICATE, etc.), then extract certificates from each PKCS#7 payload. All certificates across all blocks are returned as a single flat list.

Raises ValueError if no PEM block is found, or if any block fails to parse as PKCS#7 SignedData.

load_pkcs12_certificates

load_pkcs12_certificates(data: bytes, password: bytes = None) -> list[Certificate]

Extract certificates from a PKCS#12 / PFX archive. Non-certificate bag types (keyBag, pkcs8ShroudedKeyBag, crlBag, secretBag) are silently skipped.

password is the archive password as raw bytes (UTF-8, no NUL terminator). Pass b"" or omit for no-password archives.

Encrypted bags are supported when built with the openssl Cargo feature (PBES2/PBKDF2/AES-256-CBC) and optionally deprecated-pkcs12-algorithms (legacy 3DES). Without the openssl feature a ValueError is raised when any encrypted bag is encountered.

load_pkcs12_keys

load_pkcs12_keys(data: bytes, password: bytes = None) -> list[bytes]

Extract PKCS#8 private keys from a PKCS#12 archive. Returns a list of raw DER-encoded OneAsymmetricKey blobs, one per keyBag (unencrypted) and pkcs8ShroudedKeyBag (decrypted with the openssl feature) entry. certBag, crlBag, and secretBag entries are silently skipped.

load_pkcs12

load_pkcs12(data: bytes, password: bytes = None) -> tuple[list[Certificate], list[bytes]]

Extract both certificates and private keys from a PKCS#12 archive in a single call. Returns (certs, keys) where certs is a list[Certificate] and keys is a list[bytes] of DER-encoded PKCS#8 structures.

create_pkcs12

create_pkcs12(
    certificates: list[Certificate | bytes],
    private_key: PrivateKey | bytes | None = None,
    password: bytes | None = None,
) -> bytes

Build a DER-encoded PKCS#12 PFX archive. Each element of certificates may be a Certificate object or raw DER bytes; both types may appear in the same list. private_key may be a PrivateKey object or DER-encoded PKCS#8 bytes.

When password is None or omitted, the archive is written without encryption. When password is provided and the library is built with the openssl Cargo feature, bags are encrypted with PBES2/PBKDF2-SHA256/AES-256-CBC using 600,000 iterations. Without the openssl feature, supplying a password raises ValueError.

Raises TypeError for unrecognised element types in certificates or for an unrecognised private_key type.

Usage

import synta

# ── PKCS#7 SignedData (DER or BER) ───────────────────────────────────────────
# amazon-roots.p7b uses BER indefinite-length encoding (0x30 0x80…); both are
# handled transparently — no caller-visible difference.
data = open("bundle.p7b", "rb").read()
certs = synta.load_der_pkcs7_certificates(data)
for cert in certs:
    print(cert.subject)

# ── PKCS#7 SignedData (PEM-wrapped) ──────────────────────────────────────────
pem = open("bundle.pem", "rb").read()
certs = synta.load_pem_pkcs7_certificates(pem)

# ── PKCS#12 — certificates only ──────────────────────────────────────────────
data = open("archive.p12", "rb").read()
certs = synta.load_pkcs12_certificates(data)             # unencrypted
certs = synta.load_pkcs12_certificates(data, b"s3cr3t")  # password-protected

print(f"found {len(certs)} certificate(s)")
for cert in certs:
    print(cert.subject, cert.not_before, "–", cert.not_after)

# ── PKCS#12 — private keys only ──────────────────────────────────────────────
# Returns a list[bytes] of DER-encoded OneAsymmetricKey (PKCS#8) structures.
keys = synta.load_pkcs12_keys(data, b"s3cr3t")
for key_der in keys:
    from cryptography.hazmat.primitives.serialization import load_der_private_key
    key = load_der_private_key(key_der, None)

# ── PKCS#12 — both certificates and keys in one call ─────────────────────────
certs, keys = synta.load_pkcs12(data, b"s3cr3t")

Creating PKCS#12 Archives

import synta

# ── Certificate objects directly ─────────────────────────────────────────────
certs = synta.load_pkcs12_certificates(open("src.p12", "rb").read())
pfx = synta.create_pkcs12(certs)                        # no password
open("bundle.p12", "wb").write(pfx)

# ── With a password (openssl feature required) ────────────────────────────────
try:
    pfx = synta.create_pkcs12(certs, password=b"s3cr3t")
except ValueError:
    # Raised when password is provided without the openssl Cargo feature
    pass

# ── With a cert and a PrivateKey object ──────────────────────────────────────
certs, keys = synta.load_pkcs12(open("src.p12", "rb").read())
key = synta.PrivateKey.from_der(keys[0])
pfx = synta.create_pkcs12(certs, private_key=key, password=b"s3cr3t")

# ── Raw DER bytes also accepted ───────────────────────────────────────────────
pfx = synta.create_pkcs12(
    [cert.to_der() for cert in certs],
    private_key=keys[0],           # bytes from load_pkcs12_keys
)

# ── Roundtrip verification ────────────────────────────────────────────────────
parsed_back = synta.load_pkcs12_certificates(pfx, None)
assert len(parsed_back) == len(certs)

Encryption support: when password is provided and the library is built with --features openssl, bags are encrypted with PBES2/PBKDF2-SHA256/AES-256-CBC using 600,000 iterations. Without the openssl feature, supplying a non-None password raises ValueError.

See also PKI Blocks for format-agnostic reading, and PEM/DER for PEM encode/decode helpers.