Skip to content

Lab-09: PQC Encryption

Post-Quantum Document Encryption with ML-KEM + CSR Attestation

Section titled “Post-Quantum Document Encryption with ML-KEM + CSR Attestation”

Key Message: KEM keys can’t sign their own CSR. Enrollment evolves — attestation bridges signing and encryption identities.


“We need to send confidential documents to partners. But wait — how do we prove someone owns an encryption key if that key can’t sign anything?”

This is the attestation problem. Unlike signing keys (ML-DSA), encryption keys (ML-KEM) can only encapsulate and decapsulate — they cannot create signatures. So the traditional CSR workflow breaks.

Document encryption is essential for:

  • Confidential emails (S/MIME)
  • HR documents (salaries, evaluations)
  • Medical records (patient data)
  • Legal contracts (pre-signature)
  • Encrypted backups (offline protection)

Classical encryption (RSA, ECDH) will be broken by quantum computers. We need quantum-safe encryption today for documents that must remain confidential for years.


Traditional CSR workflow:

  1. Generate key pair
  2. Create CSR and sign it with the private key
  3. CA verifies signature = proof of possession
┌──────────────────────────────────────────────────────────────────┐
│ │
│ THE PROBLEM WITH KEM KEYS │
│ │
│ ML-KEM keys can only: │
│ ✓ Encapsulate (encrypt a shared secret) │
│ ✓ Decapsulate (decrypt a shared secret) │
│ │
│ ML-KEM keys CANNOT: │
│ ✗ Sign data │
│ ✗ Create digital signatures │
│ ✗ Prove possession via CSR signature! │
│ │
│ Solution: Use a SIGNING certificate to attest for the KEM key │
│ │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ │
│ CSR ATTESTATION WORKFLOW │
│ │
│ Step 1: Alice gets SIGNING certificate (ML-DSA-65) │
│ │
│ Step 2: Alice generates ML-KEM key pair locally │
│ │
│ Step 3: Alice creates CSR for encryption key │
│ → CSR is signed by her SIGNING key (attestation) │
│ │
│ Step 4: CA verifies: │
│ → CSR signature is valid │
│ → Signing certificate is trusted │
│ → Issues encryption cert with RelatedCertificate │
│ │
│ Result: Alice has TWO linked certificates │
│ → Signing: ML-DSA-65 (for authentication) │
│ → Encryption: ML-KEM-768 (for key encapsulation) │
│ │
└──────────────────────────────────────────────────────────────────┘

  1. Create Encryption CA
  2. Issue signing certificate (ML-DSA) 2b. Generate encryption CSR with attestation (ML-KEM) 2c. Issue encryption certificate
  3. Encrypt a document 3b. Decrypt the document

Terminal window
./journey/09-cms-encryption/demo.sh

Terminal window
qpki ca init --profile profiles/pqc-ca.yaml \
--var cn="Encryption CA" \
--ca-dir output/encryption-ca

Step 2: Issue Signing Certificate (ML-DSA-65)

Section titled “Step 2: Issue Signing Certificate (ML-DSA-65)”
Terminal window
# Generate ML-DSA-65 key pair and CSR (self-signed = proof of possession)
qpki csr gen --algorithm ml-dsa-65 \
--keyout output/alice-sign.key \
--cn "Alice" \
--out output/alice-sign.csr
qpki cert issue --ca-dir output/encryption-ca \
--profile profiles/pqc-signing.yaml \
--csr output/alice-sign.csr \
--out output/alice-sign.crt

Step 2b: Generate Encryption Key and CSR (ML-KEM-768)

Section titled “Step 2b: Generate Encryption Key and CSR (ML-KEM-768)”
Terminal window
# RFC 9883: KEM keys cannot sign, so signing cert "attests" ownership
# --attest-cert: signing certificate that vouches for this KEM key
# --attest-key: proves possession of the signing key
qpki csr gen --algorithm ml-kem-768 \
--keyout output/alice-enc.key \
--cn "Alice" \
--attest-cert output/alice-sign.crt \
--attest-key output/alice-sign.key \
--out output/alice-enc.csr

Step 2c: Issue Encryption Certificate (ML-KEM-768)

Section titled “Step 2c: Issue Encryption Certificate (ML-KEM-768)”
Terminal window
# CA verifies attestation and issues certificate
qpki cert issue --ca-dir output/encryption-ca \
--csr output/alice-enc.csr \
--profile profiles/pqc-encryption.yaml \
--attest-cert output/alice-sign.crt \
--out output/alice-enc.crt

Step 5 (Show Certificate Pair) is displayed in the demo but has no command.

Step 3: Encrypt Document (CMS EnvelopedData)

Section titled “Step 3: Encrypt Document (CMS EnvelopedData)”
Terminal window
# Hybrid encryption: AES-256-GCM (fast, symmetric) + ML-KEM (quantum-safe key exchange)
# ML-KEM encapsulates a shared secret → derives AES key → encrypts content
qpki cms encrypt \
--recipient output/alice-enc.crt \
--content-enc aes-256-gcm \
--in output/secret-document.txt \
--out output/secret-document.p7m
Terminal window
# Alice decrypts using her ML-KEM private key
qpki cms decrypt \
--key output/alice-enc.key \
--in output/secret-document.p7m \
--out output/decrypted.txt

┌─────────────────────────────────────────────────────────────────────┐
│ ALICE'S CERTIFICATE PAIR │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ SIGNING CERTIFICATE │ │ ENCRYPTION CERTIFICATE │ │
│ ├─────────────────────────────┤ ├─────────────────────────────┤ │
│ │ Algorithm: ML-DSA-65 │ │ Algorithm: ML-KEM-768 │ │
│ │ Key Usage: digitalSignature│ │ Key Usage: keyEncipherment │ │
│ │ File: alice-sign.crt │ │ File: alice-enc.crt │ │
│ │ Purpose: Sign, Attest │ │ Purpose: Receive encrypted │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ │
│ │ ▲ │
│ │ RelatedCertificate │ │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ CMS EnvelopedData (RFC 5652) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ KEMRecipientInfo (for ML-KEM) │ │
│ ├───────────────────────────────────────────────────────────────┤ │
│ │ • Recipient ID (issuer + serial) │ │
│ │ • KEM Algorithm: ML-KEM-768 │ │
│ │ • KEM Ciphertext: ~1,088 bytes │ │
│ │ • KDF: HKDF-SHA256 │ │
│ │ • Key Wrap: AES-256-WRAP │ │
│ │ • Wrapped Key: 40 bytes │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ EncryptedContentInfo │ │
│ ├───────────────────────────────────────────────────────────────┤ │
│ │ • Content Type: id-data │ │
│ │ • Algorithm: AES-256-GCM │ │
│ │ • Encrypted Content: [ciphertext + GCM tag] │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

ApproachSpeedCiphertext SizeQuantum-SafeVerdict
ML-KEM onlySlowLargeYesNot practical for large files
AES onlyFastSmallYes*Can’t share key securely
AES + ML-KEMFastSmall + ~1KBYesBest of both worlds

*AES is quantum-resistant but requires secure key exchange.


ScenarioApplicationWhy PQC?
Secure EmailS/MIME (Outlook, Thunderbird)Emails may be archived for years
HR DocumentsSalaries, performance reviewsEmployee data is sensitive
Medical RecordsHIPAA compliance50+ year retention requirements
Legal ContractsPre-signature confidentialityBusiness secrets
Encrypted BackupsOffline archivesLong-term protection

PropertyValue
NIST StandardFIPS 204
Security LevelNIST Level 3 (~192-bit classical)
Public Key~1,952 bytes
Signature~3,309 bytes
PurposeCSR attestation, message signing
PropertyValue
NIST StandardFIPS 203
Security LevelNIST Level 3 (~192-bit classical)
Public Key1,184 bytes
Ciphertext1,088 bytes
Shared Secret32 bytes
PropertyValue
Key Size256 bits
ModeGalois/Counter Mode (GCM)
AuthenticationBuilt-in (AEAD)
IV Size12 bytes
Tag Size16 bytes

  1. KEM keys cannot sign — Traditional CSR proof-of-possession doesn’t work
  2. Attestation solves this — A signing certificate vouches for the KEM key (RFC 9883)
  3. Two linked certificates — Signing (ML-DSA) + Encryption (ML-KEM) via RelatedCertificate
  4. Hybrid encryption — AES for speed + ML-KEM for quantum-safe key exchange


PQC LTV Signatures | QLAB Home | Next: Crypto-Agility →