synta-certificate 0.2.2

X.509 certificate structures for synta ASN.1 library
Documentation

synta-certificate

Table of Contents generated with DocToc

X.509 certificate structures for the synta ASN.1 library.

Certificate chain verification is in the companion crate synta-x509-verification, which implements RFC 5280 §6 path validation on top of the types provided here.

Overview

This crate provides typed X.509 v3 certificate structures based on RFC 5280. The structures integrate directly with the synta ASN.1 encoder/decoder and are designed for high-throughput parsing: the issuer, subject, and extensions fields are stored as zero-copy RawDer<'a> spans — raw DER byte slices borrowed from the input buffer — so they are decoded lazily, on demand, without any allocation at parse time.

Algorithm identification helpers return &'static str or Option<&'static str> via direct OID component matching, with no string allocation or formatting.

Features

Parsing and zero-copy decoding

  • RFC 5280 compliant — complete X.509 v3 certificate and TBSCertificate structures
  • Zero-copy parsingissuer, subject, and extensions stored as RawDer<'a> byte spans; subjectPublicKey stored as BitStringRef<'a>
  • Lazy field decoding — Distinguished Names and extensions are decoded only when accessed, eliminating parse-time overhead
  • Auto-generated from ASN.1 schemas — all types generated from formal ASN.1 definitions via synta-codegen
  • Post-quantum ready — built-in support for ML-DSA (FIPS 204) and ML-KEM (FIPS 203) algorithms

Revocation, requests, and OCSP

  • X.509 CRL support — parse Certificate Revocation Lists (RFC 5280 §5) via the crl module
  • PKCS#10 CSR support — parse Certificate Signing Requests (RFC 2986) via the csr module
  • OCSP support — parse Online Certificate Status Protocol responses (RFC 6960) via the ocsp module

Certificate bundle formats (PKCS#7 / PKCS#12)

  • PKCS#7 / CMS certificate extraction — extract certificates from SignedData bundles (RFC 5652) via certs_from_pkcs7
  • PKCS#12 certificate extraction — extract certificates from PFX archives (RFC 7292) via certs_from_pkcs12; optional OpenSSL backend for encrypted bags
  • PKCS#12 private key extraction — extract OneAsymmetricKey (PKCS#8 / RFC 5958) blobs from PFX archives via keys_from_pkcs12; same OpenSSL backend for encrypted bags
  • PKCS#8 typesOneAsymmetricKey<'a> (RFC 5958) and PrivateKeyInfo<'a> alias generated from asn1/PKCS8.asn1 via pkcs8_types module

PKI building (certificate, CSR, and PKCS#12 creation)

  • Certificate buildingCertificateBuilder + CertificateSigner trait assemble and sign a complete DER Certificate with full control over extensions, validity period, and serial number; OpensslCertificateSigner provides an OpenSSL-backed signer
  • CSR buildingCsrBuilder + CertificateSigner trait produce a DER CertificationRequest (PKCS#10, RFC 2986)
  • CRL buildingCertificateListBuilder assembles a signed TBSCertList DER with support for revoked entries (per-entry reasonCode extension), CRL-level extensions, and optional nextUpdate; CertificateListBuilder::assemble splices TBS + algorithm + signature into a complete CertificateList
  • OCSP response buildingOCSPResponseBuilder assembles a ResponseData DER for external signing via build_tbs(), then OCSPResponseBuilder::assemble wraps it with BasicOCSPResponse and the outer OCSPResponse SEQUENCE; supports byName and byKey responder identities and multiple SingleResponse entries
  • Attribute Certificate buildingAttributeCertificateBuilder assembles an AttributeCertificateInfo TBS DER (RFC 5755) from serial, validity period, issuer GeneralNames, and holder entityName; produces only the TBS portion for external signing
  • CRMF message buildingCertReqMsgBuilder assembles a single RFC 4211 CertReqMsg DER with subject, public key, proof-of-possession, and optional publication info; CertReqMessagesBuilder wraps a list of pre-encoded CertReqMsg blobs in the outer SEQUENCE OF envelope; PUB_METHOD_DONT_CARE/X500/WEB/LDAP constants select the publication method
  • CMP message buildingCMPMessageBuilder assembles a PKIMessage DER (RFC 9810) from sender/recipient GeneralName values, optional transaction ID and nonce fields, and a body payload; body setters cover ir, cr, kur, p10cr, genm, and pkiConf
  • PKCS#12 archive buildingPkcs12Builder assembles a complete PFX DER from certificates and an optional private key; delegates all cryptography to a Pkcs12Encryptor impl; OpensslPkcs12Encryptor provides PBES2/PBKDF2/AES-256-CBC encryption with a configurable Pkcs12Config (iterations, cipher, PRF, and MAC algorithm)

Crypto backends (openssl and nss features)

The crate provides a backend-agnostic key and verification layer so that callers are not exposed to backend-specific types unless they need them.

Backend-agnostic abstractions (always available)

  • PrivateKey trait — object-safe interface for private keys: public_key_spki_der() returns the DER-encoded SubjectPublicKeyInfo; as_signer(algorithm) returns a Box<dyn ErasedCertificateSigner> ready for use with CertificateBuilder::sign
  • PrivateKeyBuilder — factory for generating new keys without naming the backend type; choose the algorithm via ec(curve), rsa(bits), ed25519(), ed448(), ml_dsa(level), or ml_kem(level), then call .generate() to obtain a Box<dyn PrivateKey>
  • KeySpec — enum variant set used internally by PrivateKeyBuilder; also inspectable for algorithm-routing in custom key providers
  • BackendPrivateKey — opaque PKCS#8 DER wrapper; implements PrivateKey and caches the parsed key for O(1) as_signer() calls; supports from_pem, from_der, from_pkcs8_der_unchecked, from_ec_private_scalar (import EC key from raw scalar + affine coordinates), and from_rsa_private_components (import RSA key from CRT fields via RsaPrivateComponents); exposed mainly for storage and serialisation use cases
  • BackendPublicKey — opaque SPKI DER wrapper; backend-agnostic entry point for verify_signature(tbs, alg_id_der, sig) (dispatches to NSS when nss is enabled, otherwise OpenSSL); also exposes from_pem, from_der, from_rsa_components, from_ec_components, key_type(), key_bit_size(), and RSA/EC component extraction
  • ErasedCertificateSigner — object-safe variant of CertificateSigner; returned by PrivateKey::as_signer; Box<dyn ErasedCertificateSigner> implements CertificateSigner
  • ErasedSignatureVerifier — object-safe variant of SignatureVerifier; returned by default_signature_verifier(); Box<dyn ErasedSignatureVerifier> implements SignatureVerifier
  • default_signature_verifier() — returns the best available verifier: NSS when nss is enabled, otherwise OpenSSL, otherwise a stub that returns an error
  • default_key_id_hasher() — analogous factory for KeyIdHasher implementations

NSS backend (nss feature)

Type Description
NssSignatureVerifier Verifies X.509 signatures via VFY_VerifyDataWithAlgorithmID; supports RSA PKCS#1 / RSA-PSS / ECDSA / EdDSA / ML-DSA
NssVerifierError Error type for NssSignatureVerifier
NssSigner Signs TBS blobs from a PKCS#8 private key imported into the NSS in-memory softokn slot; supports RSA / ECDSA / EdDSA / ML-DSA (Ed448 not supported in NSS ≤ 3.121)
NssSignerError Error type for NssSigner
NssKeyIdHasher Computes SHA-1/256/384/512 via PK11_HashBuf for SKI/AKI extension encoding
NssKeyIdHasherError Error type for NssKeyIdHasher

OpenSSL backend (openssl feature)

Type Description
OpensslPrivateKey OpenSSL-backed private key implementing PrivateKey; wraps PKey<Private>
OpensslCertificateSigner Implements CertificateSigner around an OpenSSL PKey<Private>; accepts hash-name string
OpensslCertificateSignerError Error type for OpensslCertificateSigner
OpensslSignatureVerifier Verifies X.509 signatures via OpenSSL EVP; dispatches on algorithm OID
OpensslVerifierError Error type for OpensslSignatureVerifier
OpensslKeyIdHasher SHA-1/256/384/512 hashing via OpenSSL for SKI/AKI extension encoding
OpensslKeyIdHasherError Error type for OpensslKeyIdHasher

Name, extension, and algorithm helpers

  • RFC 4514-style DN formattingformat_dn walks the raw DER directly; format_dn_slash emits the openssl legacy /CN=.../O=... form used in AKI DirName output
  • Subject Alternative Name accessCertificate::subject_alt_names() returns parsed SAN entries as (tag_number, content) pairs; parse_general_names walks any raw SEQUENCE OF GeneralName DER span
  • DN attribute parsingparse_name_attrs extracts (dotted_oid, value_str) pairs from a raw Name DER span; decode_string_value decodes individual ASN.1 string values by tag, with Latin-1 normalisation for TeletexString
  • Zero-alloc OID identificationidentify_signature_algorithm and identify_public_key_algorithm return static strings
  • Public key decodingdecode_public_key_info dispatches on the algorithm OID and returns a PublicKeyInfo enum with algorithm-specific fields (RSA modulus/exponent, EC curve metadata)
  • PKCS#9 OID constants — 9 attribute OIDs from RFC 2985, RFC 5652, RFC 2986, and RFC 7292 exposed in oids (PKCS9_EMAIL_ADDRESS, PKCS9_CONTENT_TYPE, PKCS9_MESSAGE_DIGEST, PKCS9_SIGNING_TIME, PKCS9_COUNTERSIGNATURE, PKCS9_CHALLENGE_PASSWORD, PKCS9_EXTENSION_REQUEST, PKCS9_FRIENDLY_NAME, PKCS9_LOCAL_KEY_ID)
  • Extension value builders (ext_builder) — DER-encoding helpers for common X.509 v3 extension values; each returns the raw bytes of the extension's extnValue:
    • encode_basic_constraints(ca, path_length) — RFC 5280 §4.2.1.9
    • encode_key_usage(bits) — RFC 5280 §4.2.1.3; KEY_USAGE_* constants map named bits
    • encode_subject_key_identifier(spki_der, method, hasher) — RFC 5280 §4.2.1.2 / RFC 7093
    • encode_authority_key_identifier(issuer_spki_der, method, hasher) — RFC 5280 §4.2.1.1 / RFC 7093
    • KeyIdMethod enum selects the key identifier algorithm: Rfc5280Sha1 (default) or four RFC 7093 alternatives (SHA-256/384/512 with optional truncation, or hash of full SubjectPublicKeyInfo)
    • KeyIdHasher trait decouples hash computation from extension encoding; OpensslKeyIdHasher provides an OpenSSL-backed implementation (requires openssl feature)
    • Fluent extension builders — accumulate entries and produce the final extnValue DER on build():
      • SubjectAlternativeNameBuilder — Subject Alternative Name (2.5.29.17); supports dns_name, rfc822_name, uri, ip_address, directory_name, registered_id
      • IssuerAlternativeNameBuilder — Issuer Alternative Name (2.5.29.18); same GeneralName methods as SAN builder
      • AuthorityInformationAccessBuilder — Authority Information Access (1.3.6.1.5.5.7.1.1); ocsp(uri) and ca_issuers(uri) methods
      • ExtendedKeyUsageBuilder — Extended Key Usage (2.5.29.37); convenience methods for server_auth, client_auth, code_signing, email_protection, time_stamping, ocsp_signing; add_oid for custom purposes
      • NameConstraintsBuilder — Name Constraints (2.5.29.30); permit_dns/rfc822/uri/ip/directory_name and exclude_* counterparts
      • CRLDistributionPointsBuilder — CRL Distribution Points (2.5.29.31); full_name_uri, full_name_dns, full_name_directory
      • IssuingDistributionPointBuilder — Issuing Distribution Point (2.5.29.28); full_name_uri, full_name_dns, boolean flags only_contains_user_certs, only_contains_cacerts, indirect_crl, only_contains_attribute_certs
      • CertificatePoliciesBuilder — Certificate Policies (2.5.29.32); add_policy(oid) and add_policy_cps(oid, uri) with CPS qualifier

PEM encoding

  • PEM encoder/decoder — dependency-free pem_to_der / der_to_pem (RFC 7468); no external crate required

CMS cryptography (openssl feature)

  • CMS EncryptedDataOpensslEncryptor / OpensslDecryptor implement the CmsEncryptor / Decryptor traits; produce or decrypt RFC 5652 §8 EncryptedData DER with AES-CBC and a random IV
  • CMS EnvelopedDatacreate_enveloped_data encrypts plaintext for one or more recipients in one call (RSA-OAEP or RSA PKCS#1 v1.5 key wrap, AES-CBC content encryption); prepare_enveloped_data performs the same crypto but returns a pre-loaded EnvelopedDataBuilder so the caller can attach OriginatorInfo certificates/CRLs or UnprotectedAttributes before calling .build()
  • EnvelopedDataBuilder — backend-agnostic RFC 5652 §6 EnvelopedData assembler; takes pre-computed DER for the CEK algorithm, ciphertext, and each RecipientInfo; supports OriginatorInfo certs/CRLs and UnprotectedAttributes; RecipientInfos SET is sorted lexicographically per X.690 §11.6
  • BlockCipherProvider::aes_gcm_encrypt / aes_gcm_decrypt — AES-GCM (AEAD) authenticated encryption and decryption via the active crypto backend; key is 16, 24, or 32 bytes; nonce is 12 bytes; output is ciphertext ‖ 16-byte tag; optional additional authenticated data (AAD); available through default_block_cipher_provider() without naming the backend type

Protocol schema types

  • RFC 3279 algorithm parameters — DSA/DH domain parameters, DSA/ECDSA signature values, and EC domain parameter types (DssParms, EcdsaSigValue, EcParameters) in pkixalgs_types
  • Attribute Certificate v2 — complete RFC 5755 AC structure (AttributeCertificate, Holder, AttCertIssuer, IetfAttrSyntax, RoleSyntax, Clearance, AAControls, Targets) in attribute_cert_types; AttributeCertificateBuilder builds the AttributeCertificateInfo TBS DER
  • CRMF types — RFC 4211 Certificate Request Message Format (CertReqMessages, CertTemplate, ProofOfPossession, POPOSigningKey, EncryptedKey, PKIArchiveOptions) in crmf_types; CertReqMsgBuilder and CertReqMessagesBuilder provide fluent construction
  • CMP v3 types — RFC 9810 Certificate Management Protocol (PKIMessage, PKIHeader, PKIBody, PKIStatusInfo, CertRepMessage, KEM-based MAC types, CA key update types) in cmp_types; CMPMessageBuilder assembles complete PKIMessage DER

Platform compatibility

  • Python bindings — all pyo3 wrappers live in the synta-python crate; synta-certificate itself has no PyO3 dependency
  • no_std support — works in embedded and constrained environments with the alloc feature

Usage

Add to your Cargo.toml:

[dependencies]
synta-certificate = "0.1"
synta = "0.1"

Decoding a Certificate

use synta::{Decoder, Encoding};
use synta_certificate::Certificate;

let der_bytes: &[u8] = /* DER-encoded certificate */;

let mut decoder = Decoder::new(der_bytes, Encoding::Der);
let cert: Certificate = decoder.decode()?;

// serial_number is decoded eagerly
println!("Serial: {:?}", cert.tbs_certificate.serial_number);

// issuer and subject are stored as RawDer<'a> — raw DER bytes, zero-copy
let issuer_der: &[u8] = cert.tbs_certificate.issuer.as_bytes();

// format_dn walks the raw DER directly; no intermediate allocation
use synta_certificate::format_dn;
let subject_str = format_dn(cert.tbs_certificate.subject.as_bytes());
println!("Subject: {}", subject_str);

Encoding a Certificate

use synta::{Encoder, Encoding};
use synta_certificate::{Certificate, TBSCertificate, AlgorithmIdentifier, Validity};

let cert = Certificate {
    tbs_certificate: /* ... */,
    signature_algorithm: /* ... */,
    signature_value: /* ... */,
};

let mut encoder = Encoder::new(Encoding::Der);
encoder.encode(&cert)?;
let der_bytes = encoder.finish()?;

Building a Certificate

CertificateBuilder assembles a TBSCertificate, calls a CertificateSigner to obtain the signature, then wraps the result in a Certificate SEQUENCE.

The recommended approach uses the backend-agnostic PrivateKeyBuilder so no backend-specific types appear in caller code:

use synta::Integer;
use synta_certificate::{CertificateBuilder, PrivateKeyBuilder};
use synta_certificate::x509_types::Time;

let key = PrivateKeyBuilder::ec("P-256").generate()
    .expect("key generation failed");
let spki_der = key.public_key_spki_der().expect("SPKI failed");
let signer   = key.as_signer("sha256");

let cert_der: Vec<u8> = CertificateBuilder::new()
    .issuer_name(issuer_der)
    .subject_name(subject_der)
    .public_key_der(&spki_der)
    .serial_number(Integer::from(1u64))
    .not_valid_before(Time::utc_now())
    .not_valid_after(Time::utc_days_from_now(365))
    .sign(signer.as_ref())
    .expect("certificate signing failed");

When you already hold an OpenSSL PKey, OpensslCertificateSigner (requires the openssl feature) can be used directly:

use synta_certificate::{CertificateBuilder, OpensslCertificateSigner};

let pkey: openssl::pkey::PKey<_> = /* existing OpenSSL private key */;
let signer = OpensslCertificateSigner::new(&pkey, "SHA256withRSA");

let cert_der: Vec<u8> = CertificateBuilder::new()
    .issuer_name(issuer_der)
    .subject_name(subject_der)
    .public_key_der(spki_der)
    .serial_number(Integer::from(1u64))
    .not_valid_before(Time::utc_now())
    .not_valid_after(Time::utc_days_from_now(365))
    .sign(&signer)
    .expect("certificate signing failed");

Building a CSR

CsrBuilder assembles a CertificationRequestInfo, signs it, and returns a DER CertificationRequest (PKCS#10, RFC 2986). It reuses the CertificateSigner trait, so any signer that works for certificates works here too:

use synta_certificate::{CsrBuilder, OpensslCertificateSigner};

let subject_der: &[u8] = /* DER-encoded Name SEQUENCE */;
let spki_der: &[u8]    = /* DER-encoded SubjectPublicKeyInfo */;
let pkey: openssl::pkey::PKey<_> = /* OpenSSL private key */;

let signer = OpensslCertificateSigner::new(&pkey, "SHA256withRSA");

let csr_der: Vec<u8> = CsrBuilder::new()
    .subject_name(subject_der)
    .public_key_der(spki_der)
    .sign(&signer)
    .expect("CSR signing failed");

Building a PKCS#12 Archive

Pkcs12Builder assembles a PFX DER from one or more certificate DER blobs and an optional private key, delegating all cryptography to a Pkcs12Encryptor. OpensslPkcs12Encryptor (requires the openssl feature) uses PBES2/PBKDF2-SHA256 and AES-256-CBC with 600,000 iterations by default:

use synta_certificate::{Pkcs12Builder, OpensslPkcs12Encryptor};

let cert_der: Vec<u8> = /* DER-encoded certificate */;
let key_der: Vec<u8>  = /* DER-encoded PKCS#8 private key */;
let password = b"s3cr3t";

let pfx_der: Vec<u8> = Pkcs12Builder::new()
    .certificate(&cert_der)
    .private_key(&key_der)
    .build(password, &OpensslPkcs12Encryptor::new())
    .expect("PKCS#12 build failed");

std::fs::write("keystore.p12", &pfx_der).unwrap();

Without the openssl feature, pass &NoPkcs12Encryptor to produce an unencrypted archive (password must be empty):

use synta_certificate::{Pkcs12Builder, NoPkcs12Encryptor};

let pfx_der: Vec<u8> = Pkcs12Builder::new()
    .certificate(&cert_der)
    .build(b"", &NoPkcs12Encryptor)
    .expect("PKCS#12 build failed");

Generating Keys and Signing

PrivateKeyBuilder generates keys through the active crypto backend without exposing backend-specific types. The returned Box<dyn PrivateKey> works with CertificateBuilder via the ErasedCertificateSigner blanket impl:

use synta_certificate::{CertificateBuilder, PrivateKeyBuilder};
use synta_certificate::x509_types::Time;
use synta::Integer;

// Generate a new P-256 key (uses OpenSSL when that feature is enabled).
let key = PrivateKeyBuilder::ec("P-256").generate()
    .expect("EC key generation failed");

// The public half is returned as DER-encoded SubjectPublicKeyInfo.
let spki_der = key.public_key_spki_der()
    .expect("SPKI serialisation failed");

// Create a signer — "sha256" selects the hash; ignored for EdDSA keys.
let signer = key.as_signer("sha256");

let cert_der: Vec<u8> = CertificateBuilder::new()
    .issuer_name(issuer_der)
    .subject_name(subject_der)
    .public_key_der(&spki_der)
    .serial_number(Integer::from(1u64))
    .not_valid_before(Time::utc_now())
    .not_valid_after(Time::utc_days_from_now(365))
    .sign(signer.as_ref())
    .expect("certificate signing failed");

Other key types follow the same pattern:

// RSA-3072
let rsa_key = PrivateKeyBuilder::rsa(3072).generate()?;

// Ed25519 (hash-algorithm argument to as_signer is ignored)
let ed_key = PrivateKeyBuilder::ed25519().generate()?;

// Post-quantum ML-DSA-65 (requires OpenSSL 3.5+)
let pqc_key = PrivateKeyBuilder::ml_dsa("ML-DSA-65").generate()?;

BackendPrivateKey can be used instead when you need to load, store, or inspect PKCS#8 bytes:

use synta_certificate::BackendPrivateKey;

// Load from unencrypted PKCS#8 DER (requires openssl feature for parsing)
let key = BackendPrivateKey::from_der(&pkcs8_der)?;

// Load from PEM (optionally password-protected)
let key = BackendPrivateKey::from_pem(pem_bytes, None)?;
let key = BackendPrivateKey::from_pem(pem_bytes, Some(b"passphrase"))?;

// Access the raw PKCS#8 DER for storage
let pkcs8_bytes: &[u8] = /* key.pkcs8_der is pub(crate); use from_pkcs8_der_unchecked to
                            round-trip if you already hold validated bytes */;

Verifying a Certificate Signature

BackendPublicKey::verify_signature dispatches to NSS (when the nss feature is enabled) or OpenSSL, so the same call works with either backend:

use synta::{Decoder, Encoding};
use synta_certificate::{BackendPublicKey, Certificate};
use synta::traits::Encode as _;

// Parse the issuer certificate to extract its public key.
let issuer_der = std::fs::read("issuer.crt").unwrap();
let mut dec = Decoder::new(&issuer_der, Encoding::Der);
let issuer: Certificate = dec.decode().unwrap();

// Wrap the SPKI DER in a BackendPublicKey.
let mut spki_enc = synta::Encoder::new(Encoding::Der);
issuer.tbs_certificate.subject_public_key_info
    .encode(&mut spki_enc).unwrap();
let spki_der = spki_enc.finish().unwrap();
let pub_key = BackendPublicKey::from_spki_der(spki_der);

// Parse the certificate to verify.
let cert_der = std::fs::read("subject.crt").unwrap();
let mut dec = Decoder::new(&cert_der, Encoding::Der);
let cert: Certificate = dec.decode().unwrap();

// Re-encode the TBSCertificate and AlgorithmIdentifier for raw DER bytes.
let mut tbs_enc = synta::Encoder::new(Encoding::Der);
cert.tbs_certificate.encode(&mut tbs_enc).unwrap();
let tbs_der = tbs_enc.finish().unwrap();

let mut alg_enc = synta::Encoder::new(Encoding::Der);
cert.signature_algorithm.encode(&mut alg_enc).unwrap();
let alg_der = alg_enc.finish().unwrap();

let sig_bytes = cert.signature_value.as_bytes();

pub_key
    .verify_signature(&tbs_der, &alg_der, sig_bytes)
    .expect("signature verification failed");

For use cases that do not hold a BackendPublicKey, default_signature_verifier() returns a boxed ErasedSignatureVerifier backed by whichever backend is active:

use synta_certificate::default_signature_verifier;
use synta_certificate::SignatureVerifier as _;

let verifier = default_signature_verifier();
verifier.verify_certificate_signature(&tbs_der, &alg_der, &sig_bytes, &issuer_spki_der)?;

Algorithm Identification

identify_signature_algorithm and identify_public_key_algorithm use two-level integer dispatch: a length+prefix check selects the algorithm family, then a u32 match selects the variant. All discriminant values are derived from the generated oids constants via const-indexing. The functions never allocate and return &'static str.

use synta::ObjectIdentifier;
use synta_certificate::{oids, names, identify_signature_algorithm, identify_public_key_algorithm};

let oid = ObjectIdentifier::new(oids::SHA256_WITH_RSA).unwrap();
assert_eq!(identify_signature_algorithm(&oid), names::SHA256_WITH_RSA);

let oid = ObjectIdentifier::new(oids::ML_DSA_44).unwrap();
assert_eq!(identify_signature_algorithm(&oid), names::ML_DSA_44);

let oid = ObjectIdentifier::new(oids::ML_KEM_768).unwrap();
assert_eq!(identify_public_key_algorithm(&oid), Some(names::ML_KEM_768));

DN Formatting

format_dn formats a DER-encoded Name as a comma-space-separated string, matching the openssl x509 -text output style:

use synta_certificate::format_dn;

// Takes raw DER bytes of a Name (e.g. from RawDer::as_bytes())
let dn_string = format_dn(cert.tbs_certificate.subject.as_bytes());
// → "CN=example.com, O=Example Inc, C=US"

format_dn_slash produces the openssl legacy slash-prefix form used for DirName values inside Authority Key Identifier and Subject Alt Name:

use synta_certificate::format_dn_slash;

let dirn = format_dn_slash(name_der);
// → "/C=US/O=Example Inc/CN=example.com"

Public Key Decoding

decode_public_key_info inspects the algorithm OID and returns a PublicKeyInfo enum with algorithm-specific fields, so callers do not need to repeat OID dispatch for display or further processing:

use synta_certificate::{decode_public_key_info, PublicKeyInfo};

let spki = &cert.tbs_certificate.subject_public_key_info;
let info = decode_public_key_info(
    &spki.algorithm.algorithm,
    spki.algorithm.parameters.as_ref(),
    spki.subject_public_key.as_bytes(),
    spki.subject_public_key.bit_len(),
);

match info {
    PublicKeyInfo::Rsa { modulus, exponent, bit_count } => {
        println!("RSA-{}, exponent={}", bit_count, exponent);
    }
    PublicKeyInfo::Ec { bit_count, curve_nist_name, .. } => {
        println!("EC-{} ({})", bit_count, curve_nist_name.unwrap_or("?"));
    }
    PublicKeyInfo::Unknown { alg_name, .. } => {
        println!("Unknown: {}", alg_name);
    }
}

PEM Encoding and Decoding

pem_to_der decodes every -----BEGIN ...----- block in a byte slice and returns the DER bytes for each. der_to_pem encodes DER bytes as a single PEM block with a caller-supplied label. Neither function depends on any external crate.

use synta_certificate::{pem_to_der, der_to_pem};

// Decode: may contain multiple blocks (e.g. a certificate chain)
let pem_bytes = std::fs::read("chain.pem").unwrap();
let ders: Vec<Vec<u8>> = pem_to_der(&pem_bytes);

// Encode: standard RFC 7468 format, 64-char lines
let pem_output = der_to_pem("CERTIFICATE", &der_bytes);
std::fs::write("cert.pem", &pem_output).unwrap();

Common labels: "CERTIFICATE", "CERTIFICATE REQUEST", "X509 CRL", "OCSP RESPONSE".

Parsing a CRL

The crl module exposes CertificateList (RFC 5280 §5):

use synta::{Decoder, Encoding};
use synta_certificate::crl::CertificateList;

let der = std::fs::read("revoked.crl").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let crl: CertificateList = dec.decode().unwrap();

let tbs = &crl.tbs_cert_list;
println!("Issuer: {:?}", tbs.issuer);
println!("This update: {:?}", tbs.this_update);
let revoked = tbs.revoked_certificates.as_ref().map_or(0, |v| v.len());
println!("Revoked entries: {}", revoked);

Parsing a CSR

The csr module exposes CertificationRequest (RFC 2986 / PKCS#10):

use synta::{Decoder, Encoding};
use synta_certificate::csr::CertificationRequest;

let der = std::fs::read("request.csr").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let csr: CertificationRequest = dec.decode().unwrap();

let info = &csr.certification_request_info;
println!("Subject: {:?}", info.subject);
println!("Version: {:?}", info.version);

Parsing an OCSP Response

The ocsp module exposes OCSPResponse (RFC 6960):

use synta::{Decoder, Encoding};
use synta_certificate::ocsp::OCSPResponse;

let der = std::fs::read("response.der").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let resp: OCSPResponse = dec.decode().unwrap();

println!("Status: {:?}", resp.response_status);
if let Some(rb) = &resp.response_bytes {
    println!("Response type OID: {:?}", rb.response_type);
}

Extracting Certificates from PKCS#7 / CMS

certs_from_pkcs7 extracts DER-encoded certificates from a PKCS#7 SignedData bundle (RFC 5652). Both DER and BER input are accepted:

use synta_certificate::certs_from_pkcs7;

let p7b = std::fs::read("bundle.p7b").unwrap();
let certs: Vec<Vec<u8>> = certs_from_pkcs7(&p7b).unwrap();
println!("Found {} certificate(s)", certs.len());

Extracting Certificates from PKCS#12

certs_from_pkcs12 extracts DER-encoded certificates from a PKCS#12 PFX archive (RFC 7292). Pass NoCrypto to reject encrypted bags:

use synta_certificate::{certs_from_pkcs12, NoCrypto};

let pfx = std::fs::read("keystore.p12").unwrap();
let certs: Vec<Vec<u8>> = certs_from_pkcs12(&pfx, b"password", &NoCrypto).unwrap();

Enable the openssl feature to decrypt bags encrypted with PBES2/PBKDF2 + AES or legacy RC2/3DES algorithms (with deprecated-pkcs12-algorithms):

[dependencies]
synta-certificate = { version = "0.1", features = ["openssl"] }
use synta_certificate::{certs_from_pkcs12, OpensslDecryptor};

let pfx = std::fs::read("encrypted.p12").unwrap();
let certs = certs_from_pkcs12(&pfx, b"secret", &OpensslDecryptor).unwrap();

Extracting Private Keys from PKCS#12

keys_from_pkcs12 extracts OneAsymmetricKey (PKCS#8, RFC 5958) blobs from a PKCS#12 PFX archive. It follows the same decryptor pattern as certs_from_pkcs12:

use synta_certificate::{keys_from_pkcs12, NoCrypto};

let pfx = std::fs::read("keystore.p12").unwrap();
let keys: Vec<Vec<u8>> = keys_from_pkcs12(&pfx, b"password", &NoCrypto).unwrap();
println!("Found {} private key(s)", keys.len());

Enable the openssl feature for bags encrypted with PBES2/PBKDF2 + AES:

use synta_certificate::{keys_from_pkcs12, OpensslDecryptor};

let pfx = std::fs::read("encrypted.p12").unwrap();
let keys = keys_from_pkcs12(&pfx, b"secret", &OpensslDecryptor).unwrap();

Subject Alternative Names

Certificate::subject_alt_names() locates the SAN extension (OID 2.5.29.17) and returns parsed GeneralName entries as (tag_number, content) pairs. Returns an empty Vec when no SAN extension is present.

Tag numbers follow the GeneralName CHOICE (RFC 5280 §4.2.1.6):

Tag Alternative Content
0 otherName OtherName value bytes
1 rfc822Name IA5String bytes (email)
2 dNSName IA5String bytes (DNS name)
4 directoryName Complete Name SEQUENCE TLV
6 uniformResourceIdentifier IA5String bytes (URI)
7 iPAddress 4 bytes (IPv4) or 16 bytes (IPv6)
8 registeredID OID value bytes
use synta::{Decoder, Encoding};
use synta_certificate::Certificate;

let der = std::fs::read("cert.der").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let cert: Certificate = dec.decode().unwrap();

for (tag, content) in cert.subject_alt_names() {
    match tag {
        2 => println!("DNS: {}", String::from_utf8_lossy(&content)),
        7 if content.len() == 4 => {
            let octets: [u8; 4] = content.try_into().unwrap();
            println!("IP: {}", std::net::Ipv4Addr::from(octets));
        }
        _ => {}
    }
}

parse_general_names does the same walk on any raw SEQUENCE OF GeneralName DER span, e.g. an extension value retrieved by OID:

use synta_certificate::parse_general_names;

// san_der is the DER bytes of a SEQUENCE OF GeneralName
let san_der: Vec<u8> = std::fs::read("san.der").unwrap();
for (tag, content) in parse_general_names(&san_der) {
    println!("tag={} len={}", tag, content.len());
}

Parsing Distinguished Name Attributes

parse_name_attrs walks a raw Name DER span and returns one (dotted_oid, value_str) pair per AttributeTypeAndValue. String values are decoded per-tag (TeletexString → Latin-1, BMP/Universal → UTF-16 BE → UTF-8); unrecognised tags produce a #hexstring.

use synta::{Decoder, Encoding};
use synta_certificate::{Certificate, parse_name_attrs};

let der = std::fs::read("cert.der").unwrap();
let mut dec = Decoder::new(&der, Encoding::Der);
let cert: Certificate = dec.decode().unwrap();

// issuer and subject are RawDer<'a>; as_bytes() returns the Name TLV
let attrs = parse_name_attrs(cert.tbs_certificate.subject.as_bytes());
for (oid, value) in &attrs {
    println!("{} = {}", oid, value);
}
// e.g.: "2.5.4.3 = example.com", "2.5.4.10 = Example Inc", "2.5.4.6 = US"

decode_string_value decodes a single value field given its tag number and raw value bytes (tag + length already stripped):

use synta_certificate::decode_string_value;

// tag 12 = UTF8String, 19 = PrintableString, 20 = TeletexString, 22 = IA5String
let s = decode_string_value(12, b"hello");
assert_eq!(s, "hello");

CMS EncryptedData Encryption and Decryption

OpensslEncryptor implements both Encryptor (raw AES-CBC) and CmsEncryptor (full RFC 5652 §8 EncryptedData DER assembly). A fresh random IV is generated for every call via openssl::rand::rand_bytes.

use synta_certificate::{
    CmsDecryptor as _, CmsEncryptor as _,
    OpensslDecryptor, OpensslEncryptor,
    cms_rfc5652_types::EncryptedData,
    pkcs7_types, pkcs12_types,
};
use synta::{Decoder, Encoding};
use synta::traits::Encode as _;

let key: [u8; 16] = *b"0123456789abcdef";
let plaintext = b"secret payload";

// Encrypt → DER-encoded EncryptedData SEQUENCE
let der = OpensslEncryptor
    .create_encrypted_data(
        pkcs7_types::ID_DATA,         // content type (id-data)
        pkcs12_types::ID_AES128_CBC,  // cipher (AES-128-CBC, 16-byte key)
        plaintext,
        &key,
    )
    .expect("encryption failed");

// Decrypt: decode the EncryptedData, re-encode AlgorithmIdentifier to
// DER bytes, then call OpensslDecryptor.decrypt.
let mut decoder = Decoder::new(&der, Encoding::Der);
let ed: EncryptedData<'_> = decoder.decode().unwrap();

let mut alg_enc = synta::Encoder::new(Encoding::Der);
ed.encrypted_content_info
    .content_encryption_algorithm
    .encode(&mut alg_enc)
    .unwrap();
let alg_der = alg_enc.finish().unwrap();

let ciphertext = ed.encrypted_content_info.encrypted_content
    .as_ref()
    .unwrap()
    .as_bytes();

let recovered = OpensslDecryptor
    .decrypt(&alg_der, ciphertext, &key)
    .expect("decryption failed");
assert_eq!(&recovered, plaintext as &[u8]);

The Encryptor trait can also be used directly, returning (alg_id_der, ciphertext) without the EncryptedData wrapper — useful when building custom CMS structures:

use synta_certificate::{Encryptor as _, OpensslEncryptor, pkcs12_types};

let key = [0u8; 32];
let (alg_id_der, ciphertext) = OpensslEncryptor
    .encrypt(pkcs12_types::ID_AES256_CBC, b"data", &key)
    .expect("encryption failed");
// alg_id_der: DER AlgorithmIdentifier with embedded random IV
// ciphertext: raw AES-256-CBC ciphertext bytes
let _ = (alg_id_der, ciphertext);

RFC 3279 Algorithm Parameters

The pkixalgs_types module provides DSA/DH domain parameter types, DSA/ECDSA signature value types, and the EC domain parameter CHOICE as defined in RFC 3279 and X9.62. The module is standalone — it does not depend on other X.509 types.

use synta::{Decoder, Encoding, Encode as _};
use synta_certificate::pkixalgs_types::{DssParms, EcdsaSigValue, ECParameters};
use synta::Integer;

// Round-trip: encode a DssParms struct, then decode it back
let parms = DssParms {
    p: Integer::from(23u64),
    q: Integer::from(11u64),
    g: Integer::from(5u64),
};
let mut enc = synta::Encoder::new(Encoding::Der);
parms.encode(&mut enc).unwrap();
let der = enc.finish().unwrap();

let mut dec = Decoder::new(&der, Encoding::Der);
let decoded: DssParms = dec.decode().unwrap();
assert_eq!(decoded.p, parms.p);
assert_eq!(decoded.q, parms.q);
assert_eq!(decoded.g, parms.g);

// ECDSA signature value (r, s integers over the curve order)
let sig = EcdsaSigValue {
    r: Integer::from(42u64),
    s: Integer::from(17u64),
};
let mut enc = synta::Encoder::new(Encoding::Der);
sig.encode(&mut enc).unwrap();
let sig_der = enc.finish().unwrap();

let mut dec = Decoder::new(&sig_der, Encoding::Der);
let decoded_sig: EcdsaSigValue = dec.decode().unwrap();
assert_eq!(decoded_sig.r, sig.r);

// ECParameters is a CHOICE: namedCurve OID, explicit domain, or implicitlyCA
// namedCurve is the common case in modern X.509 (P-256 = 1.2.840.10045.3.1.7)
let curve_oid = synta::ObjectIdentifier::new(&[1, 2, 840, 10045, 3, 1, 7]).unwrap();
let params = ECParameters::NamedCurve(curve_oid);
let mut enc = synta::Encoder::new(Encoding::Der);
params.encode(&mut enc).unwrap();
let params_der = enc.finish().unwrap();

let mut dec = Decoder::new(&params_der, Encoding::Der);
let decoded_params: ECParameters<'_> = dec.decode().unwrap();
assert!(matches!(decoded_params, ECParameters::NamedCurve(_)));

Attribute Certificates (RFC 5755)

The attribute_cert_types module provides the full RFC 5755 Attribute Certificate v2 structure. An Attribute Certificate (AC) binds attributes (roles, clearances, service identifiers) to a holder without re-issuing the holder's Public Key Certificate.

use synta::{Decoder, Encoding};
use synta_certificate::attribute_cert_types::{AttributeCertificate, AttCertIssuer};

fn parse_ac(der_bytes: &[u8]) -> synta::Result<()> {
    let mut dec = Decoder::new(der_bytes, Encoding::Der);
    let ac: AttributeCertificate<'_> = dec.decode()?;

    let info = &ac.acinfo;
    println!("version:    {:?}", info.version);
    println!("serial:     {:?}", info.serial_number);

    // AttCertIssuer is a CHOICE: v1Form (issuerName GeneralNames) or v2Form (V2Form)
    match &info.issuer {
        AttCertIssuer::V1Form(names) => println!("v1 issuer names: {}", names.len()),
        AttCertIssuer::V2Form(_v2)   => println!("v2Form issuer"),
    }

    // Attributes carry roles, clearances, or service-auth-info
    for attr in &info.attributes {
        println!("attribute type OID: {}", attr.attr_type);
        // attr.attr_values is RawDer<'_> (lazy-decoded ANY value)
    }

    Ok(())
}

CRMF Messages (RFC 4211)

The crmf_types module provides Certificate Request Message Format types from RFC 4211. CRMF is used in CMP (Certificate Management Protocol) to carry certificate requests together with proof-of-possession evidence.

use synta::{Decoder, Encoding};
use synta_certificate::crmf_types::{CertReqMessages, ProofOfPossession};

fn parse_crmf(der_bytes: &[u8]) -> synta::Result<()> {
    let mut dec = Decoder::new(der_bytes, Encoding::Der);
    let msgs: CertReqMessages<'_> = dec.decode()?;

    for req_msg in &msgs {
        let req = &req_msg.cert_req;
        println!("certReqId: {:?}", req.cert_req_id);

        // CertTemplate carries optional subject, issuer, validity, publicKey, etc.
        let tmpl = &req.cert_template;
        if tmpl.subject.is_some() {
            // subject is Option<Name<'a>>: a parsed distinguished name
            println!("subject present");
        }
        if tmpl.validity.is_some() {
            println!("validity bounds present");
        }

        // Proof of possession: raVerified, signature, keyEncipherment, keyAgreement
        if let Some(pop) = &req_msg.popo {
            match pop {
                ProofOfPossession::Signature(_)        => println!("pop: signature"),
                ProofOfPossession::KeyEncipherment(_)  => println!("pop: key encipherment"),
                ProofOfPossession::KeyAgreement(_)     => println!("pop: key agreement"),
                ProofOfPossession::RaVerified(_)       => println!("pop: RA verified"),
            }
        }
    }
    Ok(())
}

CMP Messages (RFC 9810)

The cmp_types module provides Certificate Management Protocol v3 types from RFC 9810. CMP is used for automated certificate lifecycle management between end entities and CAs.

use synta::{Decoder, Encoding};
use synta_certificate::cmp_types::{PKIMessage, PKIBody};

fn parse_cmp(der_bytes: &[u8]) -> synta::Result<()> {
    let mut dec = Decoder::new(der_bytes, Encoding::Der);
    let msg: PKIMessage<'_> = dec.decode()?;

    let header = &msg.header;
    println!("pvno:      {:?}", header.pvno);
    // sender and recipient are GeneralName<'_>
    println!("sender:    {:?}", header.sender);
    println!("recipient: {:?}", header.recipient);

    // PKIBody is a CHOICE over 27 message types; all carry RawDer<'_> for
    // lazy decoding, except pkiconf which is NULL.
    match &msg.body {
        PKIBody::Ir(raw)  => {
            // Initialization Request: decode raw as CertReqMessages when needed
            println!("ir: {} bytes", raw.as_bytes().len());
        }
        PKIBody::Ip(raw)  => {
            println!("ip: {} bytes", raw.as_bytes().len());
        }
        PKIBody::Pkiconf(_) => println!("PKI confirmation (NULL)"),
        PKIBody::Error(raw) => {
            println!("error info: {} bytes", raw.as_bytes().len());
        }
        _ => println!("other body type"),
    }
    Ok(())
}

Encoding Extension Values

encode_basic_constraints, encode_key_usage, encode_subject_key_identifier, and encode_authority_key_identifier each return the raw DER bytes of the extension's extnValue — the content that goes inside the OCTET STRING wrapper in the Extension SEQUENCE. All functions use the code-generated ASN.1 types from the included X.509 schema and synta's Encode / Decode traits; no custom DER byte manipulation is required.

For multi-entry extensions, fluent builders are available: SubjectAlternativeNameBuilder, IssuerAlternativeNameBuilder, AuthorityInformationAccessBuilder, ExtendedKeyUsageBuilder, NameConstraintsBuilder, CRLDistributionPointsBuilder, IssuingDistributionPointBuilder, and CertificatePoliciesBuilder. Each follows the same consume-and-return pattern: chain setter calls and finish with .build() to obtain the extnValue DER bytes.

The key-identifier functions require a KeyIdHasher implementation. Pass &OpensslKeyIdHasher (requires the openssl feature) for SHA-1/256/384/512 support:

[dependencies]
synta-certificate = { version = "0.1", features = ["openssl"] }
use synta_certificate::{
    encode_basic_constraints, encode_key_usage, encode_subject_key_identifier,
    encode_authority_key_identifier,
    KeyIdMethod, OpensslKeyIdHasher,
    KEY_USAGE_DIGITAL_SIGNATURE, KEY_USAGE_KEY_CERT_SIGN, KEY_USAGE_C_RLSIGN,
};

// BasicConstraints for an end-entity certificate (cA absent, empty SEQUENCE)
let ee_bc = encode_basic_constraints(false, None);
assert_eq!(ee_bc, &[0x30, 0x00]);

// BasicConstraints for a CA with pathLen = 0
let ca_bc = encode_basic_constraints(true, Some(0));
assert_eq!(ca_bc, &[0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00]);

// KeyUsage: keyCertSign | cRLSign → BIT STRING 03 02 01 06
let ku = encode_key_usage(
    (1 << KEY_USAGE_KEY_CERT_SIGN) | (1 << KEY_USAGE_C_RLSIGN)
);
assert_eq!(ku, &[0x03, 0x02, 0x01, 0x06]);

// SubjectKeyIdentifier from a SubjectPublicKeyInfo DER blob
// Method: RFC 5280 default (SHA-1 of the BIT STRING value of subjectPublicKey)
let spki_der: &[u8] = /* ... */;
let ski = encode_subject_key_identifier(
    spki_der,
    KeyIdMethod::Rfc5280Sha1,
    &OpensslKeyIdHasher,
).expect("SKI encoding failed");

// AuthorityKeyIdentifier using RFC 7093 method 1:
// SHA-256 of the BIT STRING value, truncated to 160 bits
let aki = encode_authority_key_identifier(
    spki_der,
    KeyIdMethod::Rfc7093Method1Sha256,
    &OpensslKeyIdHasher,
).expect("AKI encoding failed");

KeyIdMethod selects the hash algorithm and input data for key identifier computation:

Variant Hash Input Output Specification
Rfc5280Sha1 SHA-1 BIT STRING value of subjectPublicKey 20 bytes RFC 5280 §4.2.1.2
Rfc7093Method1Sha256 SHA-256 BIT STRING value of subjectPublicKey first 20 bytes RFC 7093 §2 m1
Rfc7093Method2Sha384 SHA-384 BIT STRING value of subjectPublicKey first 20 bytes RFC 7093 §2 m2
Rfc7093Method3Sha512 SHA-512 BIT STRING value of subjectPublicKey first 20 bytes RFC 7093 §2 m3
Rfc7093Method4 { algorithm_oid } configurable full SubjectPublicKeyInfo DER full hash output RFC 7093 §2 m4

KeyIdHasher is a public trait so callers can plug in any hash backend:

use synta_certificate::{KeyIdHasher, KeyIdMethod};

struct MyHasher;

impl KeyIdHasher for MyHasher {
    type Error = std::convert::Infallible;

    fn hash(&self, algorithm_oid: &[u32], data: &[u8]) -> Result<Vec<u8>, Self::Error> {
        // dispatch on algorithm_oid and compute digest
        todo!()
    }
}

Building a CRL

CertificateListBuilder assembles a TBSCertList DER for external signing. After signing, CertificateListBuilder::assemble splices the TBS, algorithm identifier, and signature into a complete CertificateList:

use synta_certificate::CertificateListBuilder;

// issuer_der: DER-encoded Name SEQUENCE (e.g. from a CA certificate)
// sha256_rsa_alg_der: DER-encoded SHA256withRSA AlgorithmIdentifier

let tbs_der = CertificateListBuilder::new()
    .issuer(&issuer_der)
    .this_update("20240101120000Z")
    .next_update("20250101120000Z")
    .signature_algorithm(&sha256_rsa_alg_der)
    .revoke(&serial_bytes, "20231201000000Z", Some(1)) // keyCompromise
    .build()
    .expect("TBSCertList build failed");

// Sign tbs_der with your key, then assemble:
let crl_der = CertificateListBuilder::assemble(&tbs_der, &sha256_rsa_alg_der, &signature)
    .expect("CertificateList assemble failed");

The reason argument to revoke is optional; pass None to omit the reasonCode CRL entry extension. Use add_crl_extension for CRL-level extensions (e.g. crlNumber, authorityKeyIdentifier).

Building an OCSP Response

OCSPResponseBuilder assembles a ResponseData DER for external signing via build_tbs(). OCSPResponseBuilder::assemble produces the complete OCSPResponse SEQUENCE (including BasicOCSPResponse and id-pkix-ocsp-basic wrapping):

use synta_certificate::{OCSPResponseBuilder, SingleResponseSpec};

let tbs_der = OCSPResponseBuilder::new()
    .responder_key_hash(&key_hash_bytes)   // byKey identity
    .produced_at("20240101120000Z")
    .add_response(SingleResponseSpec {
        hash_algorithm_der: &sha1_alg_der,
        issuer_name_hash: &name_hash,
        issuer_key_hash: &key_hash_bytes,
        serial: &serial_bytes,
        status: 0,   // 0=good, 1=revoked, 2=unknown
        this_update: "20240101120000Z",
        next_update: Some("20240201120000Z"),
    })
    .build_tbs()
    .expect("ResponseData build failed");

// Sign tbs_der with your OCSP responder key, then assemble:
let ocsp_der = OCSPResponseBuilder::assemble(&tbs_der, &sig_alg_der, &signature)
    .expect("OCSPResponse assemble failed");

Use responder_name(name_der) instead of responder_key_hash to set a byName responder identity from a DER-encoded Name SEQUENCE.

Certificate Structure

Core Types

Type Description
Certificate<'a> Top-level X.509 certificate (lifetime bound to input buffer)
TBSCertificate<'a> To-be-signed certificate data
AlgorithmIdentifier<'a> Algorithm OID with optional parameters
SubjectPublicKeyInfo<'a> Public key algorithm and key bits
PublicKeyInfo Decoded public key — Rsa { modulus, exponent, bit_count }, Ec { ... }, or Unknown
Validity notBefore / notAfter validity period
Time CHOICE of UTCTime or GeneralizedTime

Key Field Types

Field Rust type Notes
tbs_certificate.issuer RawDer<'a> Zero-copy; raw DER bytes of the Name SEQUENCE
tbs_certificate.subject RawDer<'a> Zero-copy; raw DER bytes of the Name SEQUENCE
tbs_certificate.extensions Option<RawDer<'a>> Zero-copy; decoded lazily
subject_public_key_info.subject_public_key BitStringRef<'a> Zero-copy; borrowed key bits
tbs_certificate.serial_number Integer Eagerly decoded
tbs_certificate.version Option<Integer> Eagerly decoded
signature_algorithm.algorithm ObjectIdentifier Eagerly decoded

Type Aliases

Alias Underlying type RFC 5280 name
Version Integer Version
CertificateSerialNumber Integer CertificateSerialNumber
UniqueIdentifier BitString UniqueIdentifier

Supported Algorithms

Signature Algorithms

Algorithm OID prefix Standard
RSA (PKCS #1) 1.2.840.113549.1.1.* RFC 3279
ECDSA 1.2.840.10045.4.* RFC 5480
EdDSA (Ed25519) 1.3.101.112 RFC 8410
EdDSA (Ed448) 1.3.101.113 RFC 8410
ML-DSA-44/65/87 2.16.840.1.101.3.4.3.17–19 FIPS 204
DSA 1.2.840.10040.4.* FIPS 186

Public Key Algorithms

Algorithm Standard
RSA RFC 3279
ECDSA RFC 5480
EdDSA (Ed25519, Ed448) RFC 8410
ML-DSA (44, 65, 87) FIPS 204
ML-KEM (512, 768, 1024) FIPS 203
DSA FIPS 186

Cargo Features

Feature Default Description
std yes Standard library support
alloc no Allocation support for no_std environments
derive yes Enable derive macros for Encode/Decode traits
serde no Derive Serialize/Deserialize on generated types
openssl yes OpenSSL crypto backend: private key generation (PrivateKeyBuilder, BackendPrivateKey), certificate signing (OpensslCertificateSigner, OpensslPrivateKey), signature verification (OpensslSignatureVerifier), key-identifier hashing (OpensslKeyIdHasher), CMS encryption/decryption (OpensslEncryptor, OpensslDecryptor), and encrypted PKCS#12 (OpensslPkcs12Encryptor)
nss no NSS crypto backend: signature verification (NssSignatureVerifier), certificate signing (NssSigner), key-identifier hashing (NssKeyIdHasher). Takes priority over openssl when both are enabled. Use --no-default-features --features nss to select NSS as the sole backend. Requires libnss3 at build and runtime
deprecated-pkcs12-algorithms no Support legacy 3DES and RC2 PKCS#12 encryption; requires openssl

no_std Usage

[dependencies]
synta-certificate = { version = "0.1", default-features = false, features = ["alloc"] }

Code Generation

All ASN.1 types are auto-generated during build time from formal schemas in the workspace asn1/ directory using synta-codegen. To regenerate:

cargo build -p synta-certificate
Schema file Generated module Standard
asn1/X509-Certificate.asn1 crate root (Certificate, TBSCertificate, …) RFC 5280 §4
asn1/X509-CRL.asn1 crl (CertificateList, TBSCertList, IssuingDistributionPoint, …) RFC 5280 §5
asn1/PKCS10-CSR.asn1 csr (CertificationRequest, …) RFC 2986
asn1/OCSP.asn1 ocsp (OCSPResponse, BasicOCSPResponse, …) RFC 6960
asn1/PKCS7-CMS.asn1 pkcs7_types RFC 5652
asn1/PKCS12.asn1 pkcs12_types RFC 7292
asn1/PKCS8.asn1 pkcs8_types (OneAsymmetricKey, PrivateKeyInfo) RFC 5958
asn1/PKCS9.asn1 pkcs9_types (OID constants for PKCS#9 attributes) RFC 2985, RFC 5652 §11
asn1/PKIXAlgs.asn1 pkixalgs_types (DssParms, DssSigValue, EcdsaSigValue, ECParameters, OID constants) RFC 3279
asn1/AttributeCertificate.asn1 attribute_cert_types (AttributeCertificate, Holder, AttCertIssuer, IetfAttrSyntax, RoleSyntax, Clearance, AAControls, Targets) RFC 5755
asn1/CRMF.asn1 crmf_types (CertReqMessages, CertTemplate, ProofOfPossession, POPOSigningKey, EncryptedKey, PKIArchiveOptions) RFC 4211
asn1/CMP.asn1 cmp_types (PKIMessage, PKIHeader, PKIBody, PKIStatusInfo, CertRepMessage, KEM MAC types, CA key update types) RFC 9810

The build.rs post-processor applies additional patches (e.g. StringTypeMode::Borrowed for zero-copy types, extensions pattern fixup) after synta-codegen runs.

License

Licensed under either of:

at your option.

References

  • RFC 5280 — Internet X.509 PKI Certificate and CRL Profile
  • RFC 7093 — Additional Methods for Generating Key Identifiers Values
  • RFC 2986 — PKCS#10: Certification Request Syntax Specification
  • RFC 6960 — Online Certificate Status Protocol (OCSP)
  • RFC 5652 — Cryptographic Message Syntax (CMS / PKCS#7)
  • RFC 7292 — PKCS#12: Personal Information Exchange Syntax
  • RFC 7468 — Textual Encodings of PKIX Structures (PEM)
  • RFC 8410 — Algorithm Identifiers for Ed25519 and Ed448
  • FIPS 204 — ML-DSA (Module-Lattice Digital Signature Standard)
  • FIPS 203 — ML-KEM (Module-Lattice Key-Encapsulation Mechanism Standard)
  • RFC 3279 — Algorithms and Identifiers for the Internet X.509 PKI Certificate and CRL Profile
  • RFC 5755 — An Internet Attribute Certificate Profile for Authorization
  • RFC 4211 — Internet X.509 PKI Certificate Request Message Format (CRMF)
  • RFC 9810 — Internet X.509 PKI Certificate Management Protocol (CMP) v3
  • RFC 9629 — Using Key Encapsulation Mechanism (KEM) Algorithms in the CMS
  • RFC 5958 — Asymmetric Key Packages (PKCS #8 / OneAsymmetricKey)
  • RFC 2985 — PKCS #9: Selected Object Classes and Attribute Types Version 2.0