synta-certificate
Table of Contents generated with DocToc
- Overview
- Features
- Usage
- Decoding a Certificate
- Encoding a Certificate
- Algorithm Identification
- DN Formatting
- Public Key Decoding
- PEM Encoding and Decoding
- Parsing a CRL
- Parsing a CSR
- Parsing an OCSP Response
- Extracting Certificates from PKCS#7 / CMS
- Extracting Certificates from PKCS#12
- Extracting Private Keys from PKCS#12
- Subject Alternative Names
- Parsing Distinguished Name Attributes
- CMS EncryptedData Encryption and Decryption
- Certificate Structure
- Supported Algorithms
- Cargo Features
- Code Generation
- License
- References
X.509 certificate structures for the synta ASN.1 library.
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
- RFC 5280 compliant — complete X.509 v3 certificate and
TBSCertificatestructures - Zero-copy parsing —
issuer,subject, andextensionsstored asRawDer<'a>byte spans;subjectPublicKeystored asBitStringRef<'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
- Zero-alloc OID identification —
identify_signature_algorithmandidentify_public_key_algorithmreturn static strings - RFC 4514-style DN formatting —
format_dnwalks the raw DER directly;format_dn_slashemits the openssl legacy/CN=.../O=...form used in AKI DirName output - Public key decoding —
decode_public_key_infodispatches on the algorithm OID and returns aPublicKeyInfoenum with algorithm-specific fields (RSA modulus/exponent, EC curve metadata) - PEM encoder/decoder — dependency-free
pem_to_der/der_to_pem(RFC 7468); no external crate required - X.509 CRL support — parse Certificate Revocation Lists (RFC 5280 §5) via the
crlmodule - PKCS#10 CSR support — parse Certificate Signing Requests (RFC 2986) via the
csrmodule - OCSP support — parse Online Certificate Status Protocol responses (RFC 6960) via the
ocspmodule - 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 viakeys_from_pkcs12; same OpenSSL backend for encrypted bags - Subject Alternative Name access —
Certificate::subject_alt_names()returns parsed SAN entries as(tag_number, content)pairs;parse_general_nameswalks any rawSEQUENCE OF GeneralNameDER span - DN attribute parsing —
parse_name_attrsextracts(dotted_oid, value_str)pairs from a raw Name DER span;decode_string_valuedecodes individual ASN.1 string values by tag, with Latin-1 normalisation for TeletexString - 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) - PKCS#8 types —
OneAsymmetricKey<'a>(RFC 5958) andPrivateKeyInfo<'a>alias generated fromasn1/PKCS8.asn1viapkcs8_typesmodule - CMS EncryptedData encryption —
OpensslEncryptorimplements theEncryptorandCmsEncryptortraits, producing RFC 5652 §8EncryptedDataDER with AES-CBC and a random IV;OpensslDecryptorhandles the symmetric decryption side - Python bindings — all pyo3 wrappers live in the
synta-pythoncrate;synta-certificateitself has no PyO3 dependency - no_std support — works in embedded and constrained environments with the
allocfeature
Usage
Add to your Cargo.toml:
[]
= "0.1"
= "0.1"
Decoding a Certificate
use ;
use Certificate;
let der_bytes: & = /* DER-encoded certificate */;
let mut decoder = new;
let cert: Certificate = decoder.decode?;
// serial_number is decoded eagerly
println!;
// issuer and subject are stored as RawDer<'a> — raw DER bytes, zero-copy
let issuer_der: & = cert.tbs_certificate.issuer.as_bytes;
// format_dn walks the raw DER directly; no intermediate allocation
use format_dn;
let subject_str = format_dn;
println!;
Encoding a Certificate
use ;
use ;
let cert = Certificate ;
let mut encoder = new;
encoder.encode?;
let der_bytes = encoder.finish?;
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 ObjectIdentifier;
use ;
let oid = new.unwrap;
assert_eq!;
let oid = new.unwrap;
assert_eq!;
let oid = new.unwrap;
assert_eq!;
DN Formatting
format_dn formats a DER-encoded Name as a comma-space-separated string,
matching the openssl x509 -text output style:
use format_dn;
// Takes raw DER bytes of a Name (e.g. from RawDer::as_bytes())
let dn_string = format_dn;
// → "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 format_dn_slash;
let dirn = format_dn_slash;
// → "/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 ;
let spki = &cert.tbs_certificate.subject_public_key_info;
let info = decode_public_key_info;
match info
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 ;
// Decode: may contain multiple blocks (e.g. a certificate chain)
let pem_bytes = read.unwrap;
let ders: = pem_to_der;
// Encode: standard RFC 7468 format, 64-char lines
let pem_output = der_to_pem;
write.unwrap;
Common labels: "CERTIFICATE", "CERTIFICATE REQUEST", "X509 CRL",
"OCSP RESPONSE".
Parsing a CRL
The crl module exposes CertificateList (RFC 5280 §5):
use ;
use CertificateList;
let der = read.unwrap;
let mut dec = new;
let crl: CertificateList = dec.decode.unwrap;
let tbs = &crl.tbs_cert_list;
println!;
println!;
let revoked = tbs.revoked_certificates.as_ref.map_or;
println!;
Parsing a CSR
The csr module exposes CertificationRequest (RFC 2986 / PKCS#10):
use ;
use CertificationRequest;
let der = read.unwrap;
let mut dec = new;
let csr: CertificationRequest = dec.decode.unwrap;
let info = &csr.certification_request_info;
println!;
println!;
Parsing an OCSP Response
The ocsp module exposes OCSPResponse (RFC 6960):
use ;
use OCSPResponse;
let der = read.unwrap;
let mut dec = new;
let resp: OCSPResponse = dec.decode.unwrap;
println!;
if let Some = &resp.response_bytes
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 certs_from_pkcs7;
let p7b = read.unwrap;
let certs: = certs_from_pkcs7.unwrap;
println!;
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 ;
let pfx = read.unwrap;
let certs: = certs_from_pkcs12.unwrap;
Enable the openssl feature to decrypt bags encrypted with PBES2/PBKDF2 + AES
or legacy RC2/3DES algorithms (with deprecated-pkcs12-algorithms):
[]
= { = "0.1", = ["openssl"] }
use ;
let pfx = read.unwrap;
let certs = certs_from_pkcs12.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 ;
let pfx = read.unwrap;
let keys: = keys_from_pkcs12.unwrap;
println!;
Enable the openssl feature for bags encrypted with PBES2/PBKDF2 + AES:
use ;
let pfx = read.unwrap;
let keys = keys_from_pkcs12.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 ;
use Certificate;
let der = read.unwrap;
let mut dec = new;
let cert: Certificate = dec.decode.unwrap;
for in cert.subject_alt_names
parse_general_names does the same walk on any raw SEQUENCE OF GeneralName
DER span, e.g. an extension value retrieved by OID:
use parse_general_names;
// san_der is the DER bytes of a SEQUENCE OF GeneralName
let san_der: = read.unwrap;
for in parse_general_names
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 ;
use ;
let der = read.unwrap;
let mut dec = new;
let cert: Certificate = dec.decode.unwrap;
// issuer and subject are RawDer<'a>; as_bytes() returns the Name TLV
let attrs = parse_name_attrs;
for in &attrs
// 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 decode_string_value;
// tag 12 = UTF8String, 19 = PrintableString, 20 = TeletexString, 22 = IA5String
let s = decode_string_value;
assert_eq!;
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 ;
use ;
use Encode as _;
let key: = *b"0123456789abcdef";
let plaintext = b"secret payload";
// Encrypt → DER-encoded EncryptedData SEQUENCE
let der = OpensslEncryptor
.create_encrypted_data
.expect;
// Decrypt: decode the EncryptedData, re-encode AlgorithmIdentifier to
// DER bytes, then call OpensslDecryptor.decrypt.
let mut decoder = new;
let ed: = decoder.decode.unwrap;
let mut alg_enc = new;
ed.encrypted_content_info
.content_encryption_algorithm
.encode
.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
.expect;
assert_eq!;
The Encryptor trait can also be used directly, returning (alg_id_der, ciphertext)
without the EncryptedData wrapper — useful when building custom CMS structures:
use ;
let key = ;
let = OpensslEncryptor
.encrypt
.expect;
// alg_id_der: DER AlgorithmIdentifier with embedded random IV
// ciphertext: raw AES-256-CBC ciphertext bytes
let _ = ;
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 |
no | OpenSSL-backed PKCS#12 decryption and CMS encryption/decryption; enables OpensslDecryptor and OpensslEncryptor |
deprecated-pkcs12-algorithms |
no | Support legacy 3DES and RC2 PKCS#12 encryption; requires openssl |
no_std Usage
[]
= { = "0.1", = false, = ["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:
| Schema file | Generated module | Standard |
|---|---|---|
asn1/X509-Certificate.asn1 |
crate root (Certificate, TBSCertificate, …) |
RFC 5280 §4 |
asn1/X509-CRL.asn1 |
crl (CertificateList, TBSCertList, …) |
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 |
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:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
References
- RFC 5280 — Internet X.509 PKI Certificate and CRL Profile
- 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)