Skip to content

CMS Signatures & Encryption

This guide covers the Cryptographic Message Syntax (CMS) implementation for signing and encrypting data.

Cryptographic Message Syntax (CMS) is a standard format (RFC 5652) for signing and encrypting data. It supports classical algorithms (ECDSA, RSA, Ed25519, Ed448), post-quantum (ML-DSA, SLH-DSA, ML-KEM), and hybrid modes.

StandardDescription
RFC 5652Cryptographic Message Syntax (CMS)
RFC 8419EdDSA (Ed25519/Ed448) in CMS
RFC 9629Using Key Encapsulation Mechanisms in CMS
RFC 9814SLH-DSA in CMS
RFC 9880ML-KEM for CMS
RFC 9882ML-DSA in CMS
FIPS 203ML-KEM (Kyber)
FIPS 204ML-DSA (Dilithium)
FIPS 205SLH-DSA (SPHINCS+)
TypeOIDDescription
SignedData1.2.840.113549.1.7.2Digital signatures
EnvelopedData1.2.840.113549.1.7.3Encrypted data

Create a CMS SignedData signature.

Terminal window
qpki cms sign --data <file> --cert <cert> --key <key> --out <output> [flags]

Flags:

FlagDefaultDescription
--data(required)File to sign
--certSigner certificate (PEM)
--keyPrivate key (PEM, or use —hsm-config)
--credentialCredential ID (alternative to —cert/—key)
--cred-dir./credentialsCredentials directory
--out, -o(required)Output file (.p7s)
--hash(auto)Hash algorithm. Auto-selected for ML-DSA per RFC 9882. Options: sha256, sha384, sha512, sha3-256, sha3-384, sha3-512
--detachedtrueCreate detached signature (content not included)
--include-certstrueInclude signer certificate in output
--hsm-configHSM configuration file (YAML)
--key-labelHSM key label (CKA_LABEL)
--key-idHSM key ID (CKA_ID, hex)
--passphraseKey passphrase

Examples:

Terminal window
# Sign with credential (recommended)
qpki cms sign --data document.pdf --credential signer --out document.p7s
qpki cms sign --data document.pdf --cert signer.crt --key signer.key --out document.p7s
qpki cms sign --data document.pdf --cert signer.crt --key signer.key --detached=false --out document.p7s
qpki cms sign --data document.pdf --cert signer.crt --key signer.key --hash sha512 --out document.p7s
qpki cms sign --data document.pdf --cert signer.crt \
--hsm-config ./hsm.yaml --key-label "signing-key" --out document.p7s

Verify a CMS SignedData signature.

Terminal window
qpki cms verify <signature-file> [flags]

Flags:

FlagDefaultDescription
--dataOriginal data file (for detached signatures)
--caCA certificate for chain verification

Examples:

Terminal window
# Verify detached signature
qpki cms verify document.p7s --data document.pdf --ca ca.crt
qpki cms verify document.p7s --ca ca.crt
qpki cms verify document.p7s --data document.pdf

Encrypt data using CMS EnvelopedData.

Terminal window
qpki cms encrypt --recipient <cert> --in <file> --out <file> [flags]

Flags:

FlagShortDefaultDescription
--recipient-r(required)Recipient certificate(s), repeatable
--in-i(required)Input file to encrypt
--out-o(required)Output file (.p7m)
--content-encaes-256-gcmContent encryption (aes-256-gcm, aes-256-cbc, aes-128-gcm)

Supported key types:

  • RSA: Uses RSA-OAEP with SHA-256
  • EC: Uses ECDH with AES Key Wrap (HSM supported via CKM_ECDH1_DERIVE)
  • ML-KEM: Uses ML-KEM encapsulation with AES Key Wrap (post-quantum)

Examples:

Terminal window
# Encrypt for a single recipient
qpki cms encrypt --recipient bob.crt --in secret.txt --out secret.p7m
qpki cms encrypt --recipient alice.crt --recipient bob.crt --in data.txt --out data.p7m
qpki cms encrypt --recipient bob.crt --in data.txt --out data.p7m --content-enc aes-256-cbc

Decrypt CMS EnvelopedData.

Terminal window
qpki cms decrypt --key <key> --in <file> --out <file> [flags]

Flags:

FlagShortDefaultDescription
--key-kPrivate key file (PEM)
--cert-cCertificate for recipient matching
--credentialCredential ID (searches ALL versions for matching key)
--cred-dir./credentialsCredentials directory
--in-i(required)Input file (.p7m)
--out-o(required)Output file
--passphraseKey passphrase

Examples:

Terminal window
# Decrypt with credential (searches all versions)
qpki cms decrypt --credential recipient --in secret.p7m --out secret.txt
qpki cms decrypt --key bob.key --in secret.p7m --out secret.txt
qpki cms decrypt --key bob.key --passphrase "secret" --in data.p7m --out data.txt
qpki cms decrypt --key bob.key --cert bob.crt --in data.p7m --out data.txt

Note: When using --credential, QPKI searches all versions of the credential for a matching decryption key. This is essential after key rotation: data encrypted with an old key (before rotation) can still be decrypted.

Display detailed information about a CMS message.

Terminal window
qpki cms info <file>

Output includes:

  • Content type (SignedData or EnvelopedData)
  • Version, algorithms, signature/encryption details
  • Signer information (for SignedData)
  • Recipient information (for EnvelopedData)
  • Embedded certificates

Examples:

Terminal window
# Display SignedData info
qpki cms info signature.p7s
qpki cms info encrypted.p7m

Create a signing certificate for CMS signatures.

Terminal window
# ECDSA
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile ec/signing --var cn="Document Signer" --id signer
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile ml/signing --var cn="PQC Signer" --id pqc-signer
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile slh/signing --var cn="Archive Signer" --id archive-signer
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile hybrid/catalyst/signing --var cn="Hybrid Signer" --id hybrid-signer
qpki cms sign --data doc.pdf \
--cert ./credentials/signer/certificates.pem \
--key ./credentials/signer/private-keys.pem --out doc.p7s
Terminal window
# 1. Generate key
qpki key gen --algorithm ecdsa-p256 --out signer.key
qpki csr gen --key signer.key --cn "Document Signer" --out signer.csr
qpki cert issue --ca-dir ./ca --profile ec/signing --csr signer.csr --out signer.crt
qpki cms sign --data doc.pdf --cert signer.crt --key signer.key --out doc.p7s

Create an encryption certificate for CMS EnvelopedData.

Terminal window
# ECDH (classical)
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile ec/encryption --var cn="Recipient" --id recipient
qpki credential enroll --ca-dir ./ca --cred-dir ./credentials \
--profile ml/encryption --var cn="PQC Recipient" --id pqc-recipient
qpki cms encrypt --recipient ./credentials/recipient/certificates.pem \
--in secret.txt --out secret.p7m
Terminal window
# 1. Generate key
qpki key gen --algorithm ecdsa-p384 --out recipient.key
qpki csr gen --key recipient.key --cn "Recipient" --out recipient.csr
qpki cert issue --ca-dir ./ca --profile ec/encryption --csr recipient.csr --out recipient.crt
qpki cms encrypt --recipient recipient.crt --in secret.txt --out secret.p7m

AlgorithmKey TypeUse Case
ECDSA-SHA256/384/512EC P-256/384/521Classical (recommended)
RSA-SHA256/384/512RSA 2048-4096Legacy compatibility
Ed25519Ed25519Modern classical (~128-bit security)
Ed448Ed448Modern classical (~224-bit security)
ML-DSA-44/65/87ML-DSAPost-quantum
SLH-DSA-*SLH-DSAHash-based PQC
AlgorithmKey TypeUse Case
RSA-OAEPRSAClassical
ECDH + AES-KWECClassical (recommended)
ML-KEM-512/768/1024ML-KEMPost-quantum
AlgorithmKey SizeMode
AES-256-GCM256-bitAEAD (default)
AES-128-GCM128-bitAEAD
AES-256-CBC256-bitCBC

QPKI implements RFC 9882 recommendations for ML-DSA in CMS:

When signing with ML-DSA certificates, the digest algorithm is automatically selected based on the ML-DSA security level if not explicitly specified:

ML-DSA VariantSecurity LevelAuto-Selected Digest
ML-DSA-44NIST Level 1SHA-256
ML-DSA-65NIST Level 3SHA-384
ML-DSA-87NIST Level 5SHA-512

Example:

Terminal window
# Sign with ML-DSA (see Section 3 for certificate creation)
qpki cms sign --data doc.pdf --cert signer.crt --key signer.key --out doc.p7s
qpki cms sign --data doc.pdf --cert signer.crt --key signer.key --hash sha256 --out doc.p7s

During verification, QPKI checks if the digest algorithm matches the ML-DSA security level and issues warnings for suboptimal combinations:

Terminal window
# Verify a signature - warning shown if digest doesn't match ML-DSA level
qpki cms verify doc.p7s --data doc.pdf --ca ca.crt
# WARNING: ML-DSA-87 signature uses SHA-256 (RFC 9882 recommends SHA-512 for NIST Level 5)
AlgorithmOIDNotes
SHA-2562.16.840.1.101.3.4.2.1Default for ML-DSA-44, classical
SHA-3842.16.840.1.101.3.4.2.2Default for ML-DSA-65
SHA-5122.16.840.1.101.3.4.2.3Default for ML-DSA-87
SHA3-2562.16.840.1.101.3.4.2.8SHA-3 family
SHA3-3842.16.840.1.101.3.4.2.9SHA-3 family
SHA3-5122.16.840.1.101.3.4.2.10SHA-3 family

QPKI implements RFC 8419 for EdDSA algorithms (Ed25519 and Ed448) in CMS:

AlgorithmOIDSecurity LevelMode
Ed255191.3.101.112~128 bitsPure (no pre-hash)
Ed4481.3.101.113~224 bitsPure (no pre-hash)

Both Ed25519 and Ed448 operate in “pure” mode per RFC 8419:

  • Data is signed directly without pre-hashing
  • Parameters field is absent in AlgorithmIdentifier
  • Ed448 uses empty context string ("")

Example:

Terminal window
# Sign with Ed448 (see Section 3 for certificate creation)
qpki cms sign --data doc.pdf --cert signer.crt --key signer.key --out doc.p7s
qpki cms verify doc.p7s --data doc.pdf --ca ca.crt
FeatureEd25519Ed448
Security~128 bits~224 bits
Signature size64 bytes114 bytes
Public key size32 bytes57 bytes
PerformanceFasterSlower
Use caseGeneral purposeHigher security requirements

QPKI implements RFC 9814 for SLH-DSA (SPHINCS+) algorithms in CMS:

AlgorithmOIDSecurityMode
SLH-DSA-SHA2-128s2.16.840.1.101.3.4.3.20NIST Level 1Small signatures
SLH-DSA-SHA2-128f2.16.840.1.101.3.4.3.21NIST Level 1Fast signing
SLH-DSA-SHA2-192s2.16.840.1.101.3.4.3.22NIST Level 3Small signatures
SLH-DSA-SHA2-192f2.16.840.1.101.3.4.3.23NIST Level 3Fast signing
SLH-DSA-SHA2-256s2.16.840.1.101.3.4.3.24NIST Level 5Small signatures
SLH-DSA-SHA2-256f2.16.840.1.101.3.4.3.25NIST Level 5Fast signing
SLH-DSA-SHAKE-128s2.16.840.1.101.3.4.3.26NIST Level 1Small signatures
SLH-DSA-SHAKE-128f2.16.840.1.101.3.4.3.27NIST Level 1Fast signing
SLH-DSA-SHAKE-192s2.16.840.1.101.3.4.3.28NIST Level 3Small signatures
SLH-DSA-SHAKE-192f2.16.840.1.101.3.4.3.29NIST Level 3Fast signing
SLH-DSA-SHAKE-256s2.16.840.1.101.3.4.3.30NIST Level 5Small signatures
SLH-DSA-SHAKE-256f2.16.840.1.101.3.4.3.31NIST Level 5Fast signing

Per RFC 9814, the digest algorithm is auto-selected based on SLH-DSA security level:

Security LevelDigest Algorithm
128-bit (Level 1)SHA-256
192-bit (Level 3)SHA-512
256-bit (Level 5)SHA-512

All SLH-DSA variants operate in “pure” mode:

  • Data is signed directly without pre-hashing
  • Parameters field is absent in AlgorithmIdentifier
  • Empty context string per RFC 9814

Example:

Terminal window
# Sign with SLH-DSA (see Section 3 for certificate creation)
qpki cms sign --data doc.pdf --cert signer.crt --key signer.key --out doc.p7s
qpki cms verify doc.p7s --data doc.pdf --ca ca.crt
FeatureSHA2 variantsSHAKE variants
Hash functionSHA-256/SHA-512SHAKE128/SHAKE256
InteroperabilityWider supportNewer standard
PerformanceSimilarSimilar
Use caseGeneral purposeSHAKE-based systems

Terminal window
# Verify a CMS signature (classical algorithms only)
openssl cms -verify -in signature.p7s -content document.pdf -CAfile ca.crt
openssl cms -decrypt -in encrypted.p7m -inkey recipient.key -out decrypted.txt
openssl cms -sign -in document.pdf -signer signer.crt -inkey signer.key -out signature.p7s

Note: OpenSSL 3.6+ supports ML-KEM for CMS encryption/decryption (RFC 9629). For ML-DSA and SLH-DSA signatures, use qpki cms commands.


Terminal window
# Sign a contract
qpki cms sign --data contract.pdf --cert signer.crt --key signer.key --out contract.p7s
qpki cms verify contract.p7s --data contract.pdf --ca ca.crt
Terminal window
# Encrypt for recipient
qpki cms encrypt --recipient alice@example.com.crt --in message.txt --out message.p7m
qpki cms decrypt --key alice.key --in message.p7m --out message.txt
Terminal window
# Encrypt with ML-KEM (quantum-resistant)
qpki cms encrypt --recipient bob-mlkem.crt --in sensitive.doc --out sensitive.p7m

For quantum-safe encryption during the post-quantum transition, use multiple recipients with different key types. This creates an EnvelopedData with two RecipientInfos, providing defense-in-depth security.

┌─────────────────────────────────────────────────────────────┐
│ EnvelopedData │
├─────────────────────────────────────────────────────────────┤
│ RecipientInfo[0]: KeyAgreeRecipientInfo (ECDH) │
│ └─ Wrapped CEK using ECDH + AES-KW │
│ │
│ RecipientInfo[1]: KEMRecipientInfo (ML-KEM) │
│ └─ Wrapped CEK using ML-KEM encapsulation │
│ │
│ EncryptedContentInfo: │
│ └─ AES-256-GCM(CEK, plaintext) │
└─────────────────────────────────────────────────────────────┘
Terminal window
# Create encryption credentials for both algorithms
qpki credential enroll --ca-dir /path/to/ca --profile ec/encryption \
--var cn="Alice (Classical)"
qpki credential enroll --ca-dir /path/to/pqc-ca --profile ml/encryption \
--var cn="Alice (PQC)"
qpki cms encrypt \
--recipient alice-ec.crt \
--recipient alice-mlkem.crt \
--in secret.txt --out secret.p7m
qpki cms decrypt --key alice-ec.key --in secret.p7m --out decrypted.txt
qpki cms decrypt --key alice-mlkem.key --in secret.p7m --out decrypted.txt
ThreatClassical (ECDH)Post-Quantum (ML-KEM)Hybrid
Classical computerProtectedProtectedProtected
Quantum computerVulnerableProtectedProtected
Bug in ML-KEMN/AVulnerableProtected
Bug in ECDHVulnerableN/AProtected

Key insight: An attacker must break BOTH algorithms to decrypt the message, providing “belt and suspenders” security during the PQC transition.


CMS operations support HSM-stored keys for both signing and decryption.

Terminal window
export HSM_PIN="****"
qpki cms sign --data document.pdf --cert signer.crt \
--hsm-config ./hsm.yaml --key-label "signing-key" --out document.p7s

For EC encryption certificates, decryption uses ECDH key derivation via CKM_ECDH1_DERIVE:

Terminal window
export HSM_PIN="****"
qpki cms decrypt --in secret.p7m --out secret.txt \
--hsm-config ./hsm.yaml --key-label "encryption-key"

Requirements for HSM decryption:

  • EC keys must have CKA_DERIVE attribute (QPKI sets this automatically)
  • HSM must support CKM_ECDH1_DERIVE mechanism

  • TSA - Timestamping for long-term validity
  • Credentials - Signing and encryption credentials
  • HSM - Hardware Security Module integration
  • RFC 5652 - CMS specification
  • RFC 8419 - EdDSA (Ed25519/Ed448) in CMS
  • RFC 9880 - ML-KEM for CMS
  • RFC 9882 - ML-DSA in CMS