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
| Constant | Value | Effect |
|---|---|---|
NONE | 0 | Embed content; include signer cert; add attrs |
DETACHED | 0x40 | Detach content (not embedded) |
BINARY | 0x80 | Preserve binary content without CRLF conversion |
NOSMIMECAP | 0x200 | Omit S/MIME capabilities from signed attributes |
NOCERTS | 0x2 | Do not embed the signer certificate |
NOATTR | 0x100 | Omit all signed attributes |
CmsVerifyFlags
| Constant | Value | Effect |
|---|---|---|
NONE | 0 | Full verification (signature + chain) |
NO_SIGNER_CERT_VERIFY | 0x20 | Skip signer certificate verification |
NOVERIFY | 0x20 | Skip all verification (decode only); same numeric value as NO_SIGNER_CERT_VERIFY |
NOINTERN | 0x10 | Do not look for signers in embedded certs |
NOCRL | 0x2000 | Ignore 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.