Color scheme: Light
You are here: start » en » Documentation » Post-Quantum Cryptography » WKL.System.Security.Cryptography overview » How-to guides » Encrypt CMS messages

Encrypt CMS messages

You encrypt data for a recipient whose certificate carries an ML-KEM key, for example for an archive whose content must stay confidential even against future quantum computers.

Solution overview

ElementPurpose
PostQuantumConfig.Exemptionswith PurePostQuantum, allows pure ML-KEM recipients and the ML-DSA issuer
MLKemKey.Generategenerates an ML-KEM key pair (ML-KEM-768 or ML-KEM-1024; ML-KEM-512 only with ReducedParameterSets)
X509CertificateBuilder with an ML-KEM keyissues the recipient certificate
AttachPrivateMLKemKeyassociates the recipient certificate with its private key
KemEnvelopedCms.Encrypt, Encodeencrypts the content for one or more recipients
KemEnvelopedCms.Decode, Decryptdecrypts at the recipient

KemEnvelopedCms is modeled on the .NET EnvelopedCms class.

Implementation

BSI TR-02102-1 recommends ML-KEM only in combination with a classical algorithm, using the CatKDF or KeyCombine combiners. A KEMRecipientInfo as defined in RFC 9629 uses ML-KEM on its own, and composite ML-KEM combines with SHA3-256. Both kinds of recipients therefore require an exemption, which you set once when your app starts. This example uses PurePostQuantum for the ML-KEM recipient and for the pure ML-DSA certification authority:

PostQuantumConfig.Exemptions |= PostQuantumExemptions.PurePostQuantum;

Create the recipient certificate. An ML-KEM key can't sign, so an ML-DSA issuer issues the certificate. In this example, that's a CA of your own:

var now = DateTimeOffset.UtcNow;
using var caKey = MLDsaKey.Generate(MLDsaParameterSet.MLDsa65);
var caRequest = new MLDsaX509SignatureGenerator(caKey).CreateCertificateRequest("CN=Contoso PQ CA");
caRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
caRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign, true));
using var ca = caRequest.CreateSelfSigned(now, now.AddYears(10), CryptoMode.PostQuantum);
 
using var archiveKey = MLKemKey.Generate(MLKemParameterSet.MLKem768);
var archiveBuilder = new X509CertificateBuilder(
    new X500DistinguishedName("CN=Contoso Archive"), archiveKey.ExportSubjectPublicKeyInfo(), default);
archiveBuilder.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, true));
using var archive = archiveBuilder.Create(
    ca, now, now.AddYears(1), RandomNumberGenerator.GetBytes(16), CryptoMode.PostQuantum);
archive.AttachPrivateMLKemKey(archiveKey);

Encrypt and decrypt:

var outgoing = new KemEnvelopedCms(new ContentInfo("Contract 2026-17"u8.ToArray()));
outgoing.Encrypt(archive);
byte[] envelope = outgoing.Encode();
 
var incoming = new KemEnvelopedCms();
incoming.Decode(envelope);
incoming.Decrypt(archive);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(incoming.ContentInfo.Content));

Remarks

  • Readable only with KemEnvelopedCms. The .NET EnvelopedCms class can't decode messages that contain a KEMRecipientInfo, not even for an RSA recipient of the same message.
  • Hybrid through composite ML-KEM. If the recipient certificate carries a composite ML-KEM key, the content stays confidential as long as either part holds. In that case, associate the private key with AttachPrivateCompositeMLKemKey. Encrypting for such a recipient requires PostQuantumExemptions.NonRecommendedCombiners. Decrypting existing messages doesn't require an exemption and raises the PolicyDeviation event.
  • Hybrid without CMS. For your own protocol without CMS, HybridKeyAgreement agrees on a key from ECDH and ML-KEM with KeyCombine, without any exemption; see Agree on a hybrid key.
  • RSA recipients in the same message. A recipient with an RSA certificate gets a KeyTransRecipientInfo with RSAES-OAEP. That recipient also opens the message with KemEnvelopedCms.
  • The private key must be in the process. Decrypt needs the recipient's private ML-KEM key. If it's missing, the call fails with a CryptographicException.
en/wiki/pqcrypt/lib/how-to/encrypt-cms-messages.txt · Last modified: by Wolfgang van der Stille