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

CMS — Cryptographic Message Syntax

native_ossl::cms wraps OpenSSL’s CMS (RFC 5652) implementation. The primary use case is SignedData — producing and verifying cryptographically signed messages that carry an X.509 signer certificate alongside the payload.

Signing a message

#![allow(unused)]
fn main() {
use native_ossl::cms::{CmsContentInfo, CmsSignFlags};

// cert: &X509, key: &Pkey<Private>
let payload = b"hello, PKI";
let signed = CmsContentInfo::sign(
    cert,
    key,
    &[],                              // no extra certs to embed
    payload,
    CmsSignFlags::BINARY | CmsSignFlags::NOSMIMECAP,
)?;

// Serialise to DER for transport.
let der = signed.to_der()?;
}

CmsSignFlags::BINARY preserves binary content without line-ending conversion. CmsSignFlags::NOSMIMECAP omits the legacy S/MIME capabilities attribute, keeping the output smaller.

To embed intermediate CA certificates for chain building by the verifier, pass them as the extra_certs slice:

#![allow(unused)]
fn main() {
let signed = CmsContentInfo::sign(cert, key, &[intermediate_ca], payload, CmsSignFlags::BINARY)?;
}

Verifying a signed message

#![allow(unused)]
fn main() {
use native_ossl::cms::{CmsContentInfo, CmsVerifyFlags};
use native_ossl::x509::X509Store;

let parsed = CmsContentInfo::from_der(&der)?;

let mut store = X509Store::new()?;
store.add_cert(&trust_anchor)?;         // the trusted root CA

let content: Vec<u8> = parsed.verify(&store, &[], CmsVerifyFlags::NONE)?;
assert_eq!(content, payload);
}

verify returns the verified plaintext on success, or Err(ErrorStack) if signature verification fails, the certificate chain cannot be validated, or the content is missing.

Pass additional untrusted certificates in the second argument to help chain building without adding them to the trust store.

Inspecting a CmsContentInfo

#![allow(unused)]
fn main() {
// OID of the outer contentType (SignedData = 1.2.840.113549.1.7.2)
let oid = cms.content_type_oid();

// OID of the encapsulated data type (ordinary data = 1.2.840.113549.1.7.1)
let eoid = cms.econtent_type_oid();

// Embedded content bytes (None if detached)
let bytes: Option<Vec<u8>> = cms.content();

// Detached vs embedded
let detached: bool = cms.is_detached();

// Signer certificates embedded in the structure
let signers = cms.signers();
for si in &signers {
    if let Some(cert) = si.signer_cert() {
        println!("signer: {:?}", cert.subject_name());
    }
}

// All certificates embedded (get1 — each is independently ref-counted)
let certs: Vec<X509> = cms.certs();

// CRLs embedded
let crls: Vec<X509Crl> = cms.crls();
}

Using an explicit library context

Both sign and from_der have _in variants that bind algorithm fetches to an explicit Arc<LibCtx>. Use these for FIPS isolation or when the pkcs11 provider is loaded in a custom context:

#![allow(unused)]
fn main() {
let signed = CmsContentInfo::sign_in(&ctx, cert, key, &[], payload, CmsSignFlags::BINARY)?;
let parsed = CmsContentInfo::from_der_in(&ctx, &der)?;
}

PKINIT eContentType matching

PKINIT (RFC 4556) uses custom eContentType OIDs (e.g. id-pkinit-authData = 1.3.6.1.5.2.3.1). Use the Oid type for comparison:

#![allow(unused)]
fn main() {
use native_ossl::obj::Oid;

let pkinit_auth_data = Oid::from_text("1.3.6.1.5.2.3.1").unwrap();
assert_eq!(cms.econtent_type_oid(), pkinit_auth_data);
}

Oid::from_text works for any dotted decimal OID, including those not registered in OpenSSL’s NID table. See the Object Identifiers guide for details.

Flags reference

CmsSignFlags

ConstantValueEffect
NONE0Embed content; include signer cert; add attrs
DETACHED0x40Detach content (not embedded)
BINARY0x80Preserve binary content without CRLF conversion
NOSMIMECAP0x200Omit S/MIME capabilities from signed attributes
NOCERTS0x2Do not embed the signer certificate
NOATTR0x100Omit all signed attributes

CmsVerifyFlags

ConstantValueEffect
NONE0Full verification (signature + chain)
NO_SIGNER_CERT_VERIFY0x20Skip signer certificate verification
NOVERIFY0x20Skip all verification (decode only); same numeric value as NO_SIGNER_CERT_VERIFY
NOINTERN0x10Do not look for signers in embedded certs
NOCRL0x2000Ignore embedded CRLs for revocation

NO_SIGNER_CERT_VERIFY and NOVERIFY share the value 0x20 (matching the underlying CMS_NO_SIGNER_CERT_VERIFY / CMS_NOVERIFY OpenSSL flags, which are defined identically). Either constant produces the same behaviour when passed to verify.