Certificates
Purpose
The noxtls-x509 crate (re-exported through the workspace TLS stack where enabled) provides DER X.509 parsing, PEM ↔ DER helpers, hostname matching for TLS-style identity checks, and path validation with optional policy knobs. Use it when you load trust anchors and peer certificates from disk, firmware blobs, or embedded PEM strings, then need to parse, inspect, and validate them before trusting a connection.
Typical flow:
- Load bytes (DER as-is, or PEM text converted to DER).
noxtls_parse_certificateto obtain aCertificateview (borrowed slices into your buffer where possible).noxtls_certificate_matches_hostname(or SAN inspection) for the intended DNS name.noxtls_validate_certificate_chain(ornoxtls_validate_certificate_chain_with_options) against your trust anchor set and a validation time string.
For PEM labels, line wrapping, and file helpers, see Utility and the broader X.509 topic.
Loading: DER vs PEM
- DER (binary) — Pass the file or blob bytes directly to
noxtls_parse_certificate(&der). The parser expects a single X.509Certificatestructure (tag0x30at the top level). - PEM (
-----BEGIN CERTIFICATE-----) — PEM is UTF-8 text. Decode withnoxtls_certificate_pem_to_der(pem: &str) -> Result<Vec<u8>>, which extracts the firstCERTIFICATEblock. For bundles (server chain files with several certificates in order), usenoxtls_certificate_chain_pem_to_der_blocks(pem: &str) -> Result<Vec<Vec<u8>>>andnoxtls_parse_certificateon each DER block in order (leaf first if that is how your file is written).
With std enabled on noxtls-x509, you can read a file and branch on PEM vs DER the same way as the noxtls_parse_certificate example: if bytes start with b"-----BEGIN CERTIFICATE-----", decode as UTF-8 then noxtls_certificate_pem_to_der; otherwise treat the bytes as DER. Optional noxtls_pem_file_to_der / noxtls_pem_file_to_der_blocks (from the same crate surface) read PEM directly from a path.
Certificate snapshot
noxtls_parse_certificate returns Certificate<'a> with fields used by validation and TLS wiring, including:
- Validity —
not_before,not_after(string forms as extracted from the cert). - Subject / issuer — Raw DER slices (
subject_raw,issuer_raw) plus parsed Subject Alternative Name DNS names insubject_alt_dns_names. - Keys and signatures —
subject_public_key, algorithm OIDs,signature_value, andraw_tbs_derfor signature verification. - Extensions — Basic constraints, key usage, EKU, name constraints, AIA/CRL distribution fields, and related OIDs as parsed by this implementation.
Inspect these fields for logging, policy, or custom checks beyond the built-in chain validator.
Rust API
- Crate:
noxtls-x509 - Module path: types and functions are re-exported at the
noxtls_x509crate root (flat API). - Primary symbols (certificates):
noxtls_parse_certificate,Certificatenoxtls_certificate_pem_to_der,noxtls_certificate_chain_pem_to_der_blocks,noxtls_certificate_der_to_pemnoxtls_certificate_matches_hostnamenoxtls_validate_certificate_chain,noxtls_validate_certificate_chain_with_options,noxtls_validate_certificate_chain_constraints_only,noxtls_validate_certificate_chain_strictnoxtls_verify_certificate_signatureValidationOptions,ValidationReport,ValidationError
Functions and types (selected):
noxtls_parse_certificate(input: &[u8]) -> Result<Certificate<'_>>— Parameters: full DER certificate bytes (size-limited internally). Behavior: parses TBSCertificate, signature, and common extensions. Returns: aCertificateview ornoxtls_core::Erroron parse failure.noxtls_certificate_pem_to_der(pem: &str) -> Result<Vec<u8>>— Parameters: PEM text containing aCERTIFICATEblock. Behavior: decodes the first block to DER. Returns: DER bytes suitable fornoxtls_parse_certificate, or PEM/DER decode errors from thenoxtls_pemlayer.noxtls_certificate_chain_pem_to_der_blocks(pem: &str) -> Result<Vec<Vec<u8>>>— Parameters: PEM text that may contain multipleCERTIFICATEblocks. Behavior: returns one DER blob per block in order. Returns: vector of DER chunks.noxtls_certificate_matches_hostname(cert, hostname: &str) -> bool— Parameters: parsedCertificateand a DNS hostname. Behavior: matches against SAN dNSName entries when present; otherwise falls back to Subject CN when no SAN DNS names exist. Returns:trueif a match is allowed under the implementation’s DNS rules (including simple wildcard handling where supported).noxtls_validate_certificate_chain(leaf, intermediates, trust_anchors, now) -> Result<ValidationReport, ValidationError>— Parameters: parsedCertificatereferences for the end-entity, optional intermediate issuers, non-empty trust anchors, andnowas an ASN.1 time string (UTCTimeYYMMDDhhmmssZor GeneralizedTimeYYYYMMDDhhmmssZ). Behavior: builds a chain toward an anchor, checks time and path constraints, and verifies signatures at each hop. Returns:ValidationReporton success, orValidationErroron failure (expired cert, untrusted root, bad signature, etc.).noxtls_validate_certificate_chain_with_options(..., options: &ValidationOptions)— Same as above with optional policy OID, required EKU OID, explicit-policy, CRL/AIA presence flags, and policy mapping inhibition.noxtls_validate_certificate_chain_constraints_only(...)— Same path-building and constraint checks without signature verification (narrow use; prefer full validation for security).noxtls_verify_certificate_signature(certificate, issuer) -> Result<(), ValidationError>— Verifies thatcertificate’s signature overraw_tbs_derverifies underissuer’s public key (RSA/EC/Ed25519 per supported OIDs).
Feature flags and policy
Certificate and X.509 code paths are gated by feature-cert on noxtls-core (and thus on crates that depend on noxtls-x509 in the full TLS profile). Your firmware or host binary should enable the noxtls-core profile that includes feature-cert when you ship TLS with PKIX verification. See Build configuration and the Configuration guide.
Examples
Parse DER from memory
use noxtls_x509::noxtls_parse_certificate;
fn inspect_leaf(der: &[u8]) -> noxtls_core::Result<()> {
let cert = noxtls_parse_certificate(der)?;
println!("not_before={} not_after={}", cert.not_before, cert.not_after);
println!("san_dns={:?}", cert.subject_alt_dns_names);
Ok(())
}
# fn main() {}
Decode PEM, then parse (host / std)
use noxtls_core::Result;
use noxtls_x509::{noxtls_certificate_pem_to_der, noxtls_parse_certificate, noxtls_certificate_matches_hostname};
fn load_leaf_from_pem(pem_utf8: &str) -> Result<()> {
let der = noxtls_certificate_pem_to_der(pem_utf8)?;
let cert = noxtls_parse_certificate(&der)?;
let _ = noxtls_certificate_matches_hostname(&cert, "server.example.com");
Ok(())
}
# fn main() {}
PEM chain: split blocks, parse each certificate
use noxtls_core::Result;
use noxtls_x509::{noxtls_certificate_chain_pem_to_der_blocks, noxtls_parse_certificate};
fn load_chain(pem: &str) -> Result<()> {
let der_blocks = noxtls_certificate_chain_pem_to_der_blocks(pem)?;
for der in &der_blocks {
let _cert = noxtls_parse_certificate(der)?;
}
Ok(())
}
# fn main() {}
Lifetime note: each Certificate<'a> borrows from the der slice you parsed. Keep the Vec<u8> (or full PEM buffer) alive for as long as you hold the Certificate views—often you store der_blocks in a struct next to the parsed cert list.
Validate against a trust anchor (generated demo)
This mirrors the cert_app example: same key material produces a self-signed “leaf” that is also your trust anchor, so path validation can succeed for API testing.
use noxtls_core::Result;
use noxtls_crypto::P256PrivateKey;
use noxtls_x509::{
noxtls_certificate_matches_hostname, noxtls_parse_certificate, noxtls_validate_certificate_chain,
noxtls_write_self_signed_certificate_p256_sha256,
};
fn demo() -> Result<()> {
let der = {
let key = P256PrivateKey::from_bytes([0x66u8; 32])?;
let pub_key = key.public_key()?;
noxtls_write_self_signed_certificate_p256_sha256(
&[0x20],
"server.noxtls.local",
"240101000000Z",
"300101000000Z",
&pub_key,
&key,
)?
};
let cert = noxtls_parse_certificate(&der)?;
assert!(noxtls_certificate_matches_hostname(&cert, "server.noxtls.local"));
let anchor = cert.clone();
let report = noxtls_validate_certificate_chain(&cert, &[], &[anchor], "20260101000000Z")
.expect("demo: self-signed cert validates as its own trust anchor");
assert!(report.chain_len >= 1);
Ok(())
}
# fn main() { let _ = demo(); }
For more issuance and CSR flows, run the in-tree examples (for example cargo run -p noxtls --example cert_app, noxtls_parse_certificate, verify_chain) and read X.509.
Security and compatibility
- Trust store — Ship only anchors you intend to trust; prefer pinning or a minimal curated store over “every public CA” on constrained devices. Document anchor rotation and emergency update paths.
- Time — Pass a correct
nowstring from your RTC or time sync; wrong clocks cause spurious not yet valid / expired results. - Hostname —
noxtls_certificate_matches_hostnameis one layer; still run full chain validation and apply TLS SNI / application policy separately. - Revocation — Built-in validation can enforce CRL/AIA presence via
ValidationOptions; it does not by itself fetch CRLs or OCSP over the network. Plan how your product obtains revocation status if required. - Errors — Parsing failures use
noxtls_core::Error; chain validation usesValidationError. Map both in application logging without leaking sensitive PEM in logs.
Related
- X.509 topic
- Utility (PEM encoding helpers)
- Build configuration