QianHeng乾珩 PQC Docs Hub量子文档 ✦ Ask AI✦ 问问文档 ⚐ Scan⚐ 扫一扫

Code Lab (runnable)

Copy-paste PQC recipes you can run today. The OpenSSL examples below were executed on OpenSSL 3.6.1 (native ML-KEM / ML-DSA / SLH-DSA, available since 3.5) and the outputs shown are the real results — note how the sizes match the FIPS parameter sets documented elsewhere on this site.

Prerequisites
OpenSSL 3.5 or newer for native PQC (openssl version). The liboqs Python section additionally needs liboqs-python. Nothing here touches production keys.

0. Confirm PQC support

openssl version
openssl list -public-key-algorithms | grep -Ei 'ML-DSA|SLH-DSA|ML-KEM'
openssl list -kem-algorithms | grep -i mlkem
openssl list -signature-algorithms | grep -Ei 'ML-DSA|SLH-DSA'
OpenSSL 3.6.1 27 Jan 2026
  { ... id-alg-ml-kem-768, ML-KEM-768, MLKEM768 } @ default
  X25519MLKEM768 @ default          # hybrid group used by TLS 1.3
  { ... id-ml-dsa-65, ML-DSA-65, MLDSA65 } @ default
  { ... id-slh-dsa-sha2-128s, SLH-DSA-SHA2-128s } @ default

Different distributions, providers, and builds may display different aliases; rely on your own openssl list output as the source of truth.

1. ML-KEM-768 — key encapsulation

Establish a shared secret the post-quantum way: the recipient publishes an encapsulation key; the sender encapsulates a fresh 32-byte secret to it; the recipient decapsulates the same secret.

# recipient: keypair
openssl genpkey -algorithm ML-KEM-768 -out mlkem.key
openssl pkey -in mlkem.key -pubout -out mlkem.pub

# sender: encapsulate a shared secret to the public key
openssl pkeyutl -encap -inkey mlkem.pub -pubin -out ct.bin -secret ss_sender.bin

# recipient: decapsulate to recover the same secret
openssl pkeyutl -decap -inkey mlkem.key -in ct.bin -secret ss_recv.bin
cmp -s ss_sender.bin ss_recv.bin && echo "shared secrets MATCH"
ciphertext: 1088 bytes,  shared secret: 32 bytes
shared secrets MATCH        # ML-KEM-768 sizes per FIPS 203

2. ML-DSA-65 — sign & verify

openssl genpkey -algorithm ML-DSA-65 -out mldsa.key
openssl pkey -in mldsa.key -pubout -out mldsa.pub
echo "hello pqc world" > msg.txt
openssl pkeyutl -sign   -inkey mldsa.key -rawin -in msg.txt -out msg.sig
openssl pkeyutl -verify -pubin -inkey mldsa.pub -rawin -in msg.txt -sigfile msg.sig
3309 byte signature        # ML-DSA-65 per FIPS 204
Signature Verified Successfully

3. SLH-DSA — the conservative, large-signature option

openssl genpkey -algorithm SLH-DSA-SHA2-128s -out slh.key
echo hi > m.txt
openssl pkeyutl -sign -inkey slh.key -rawin -in m.txt -out slh.sig
wc -c slh.sig
7856 slh.sig               # SLH-DSA-128s signature per FIPS 205 — ~2.4x larger than ML-DSA-65 at this parameter set; other SLH-DSA parameter sets can be far larger,
                           # and signing is much slower. Use where hash-only assurance matters most.

4. Hybrid TLS 1.3 in the wild

Force the post-quantum hybrid group and confirm a real server negotiates it (Cloudflare supports it):

echo | openssl s_client -groups X25519MLKEM768 -connect cloudflare.com:443 \
  -servername cloudflare.com 2>/dev/null | grep -iE 'Negotiated TLS1.3 group|Protocol'
Negotiated TLS1.3 group: X25519MLKEM768
Protocol: TLSv1.3

Support status at real public endpoints varies with server configuration and middleboxes; a failed negotiation does not necessarily mean OpenSSL lacks support — the site may simply not offer that group, or a downgrading/intercepting device may sit on the path.

To inventory your own endpoints, use scan-tls.sh from the Discovery page.

5. A self-signed ML-DSA certificate

openssl req -x509 -new -newkey ML-DSA-65 -keyout ca.key -out ca.crt \
  -days 365 -nodes -subj "/CN=pqc-demo"
openssl x509 -in ca.crt -noout -text | grep -i 'Signature Algorithm' | head -1
Signature Algorithm: ML-DSA-65

This certificate is fine for local experiments; many clients, proxies, load balancers, and certificate-handling tools may still not accept an ML-DSA certificate chain. Test end-to-end before production.

6. liboqs (Python) — algorithm-level API

For experimenting across the full algorithm set (including HQC, Falcon, McEliece) the Open Quantum Safe binding is convenient. How liboqs-python installs varies by platform and version; if the pip install fails, follow the current liboqs-python README. Some environments need CMake, a compiler, and the liboqs C library installed first. The pip command below is just one possible install path, shown for illustration.

pip install liboqs-python
import oqs

with oqs.KeyEncapsulation("ML-KEM-768") as kem:
    pk = kem.generate_keypair()
    ct, ss_sender = kem.encap_secret(pk)
    ss_recv = kem.decap_secret(ct)
    assert ss_sender == ss_recv

with oqs.Signature("ML-DSA-65") as sig:
    pk = sig.generate_keypair()
    s = sig.sign(b"hello pqc world")
    assert sig.verify(b"hello pqc world", s, pk)

print(oqs.get_enabled_kem_mechanisms()[:5])
print(oqs.get_enabled_sig_mechanisms()[:5])

Standards & references

Lab only
These are learning recipes — generate throwaway keys in a scratch directory. For production, use validated modules, constant-time builds, and hardware key protection (see Side-Channels).

实操实验室

可直接复制运行的 PQC 实操配方。下面的 OpenSSL 示例在 OpenSSL 3.6.1(自 3.5 起原生支持 ML-KEM / ML-DSA / SLH-DSA)上实跑,所示输出为真实结果——注意尺寸与本站其他页给出的 FIPS 参数集完全吻合。

前置条件
原生 PQC 需 OpenSSL 3.5 或更新openssl version)。liboqs 的 Python 部分另需 liboqs-python。本页不涉及任何生产密钥。

0 确认 PQC 支持

openssl version
openssl list -public-key-algorithms | grep -Ei 'ML-DSA|SLH-DSA|ML-KEM'
openssl list -kem-algorithms | grep -i mlkem
openssl list -signature-algorithms | grep -Ei 'ML-DSA|SLH-DSA'
OpenSSL 3.6.1 27 Jan 2026
  { ... id-alg-ml-kem-768, ML-KEM-768, MLKEM768 } @ default
  X25519MLKEM768 @ default          # TLS 1.3 使用的混合组
  { ... id-ml-dsa-65, ML-DSA-65, MLDSA65 } @ default
  { ... id-slh-dsa-sha2-128s, SLH-DSA-SHA2-128s } @ default

不同发行版 / provider / 构建可能显示不同别名,应以本机 openssl list 输出为准。

1 ML-KEM-768 密钥封装

用后量子方式协商共享密钥:接收方公布封装公钥;发送方向其封装一个全新的 32 字节密钥;接收方解封装得到相同密钥。

# 接收方:生成密钥对
openssl genpkey -algorithm ML-KEM-768 -out mlkem.key
openssl pkey -in mlkem.key -pubout -out mlkem.pub

# 发送方:用公钥封装共享密钥
openssl pkeyutl -encap -inkey mlkem.pub -pubin -out ct.bin -secret ss_sender.bin

# 接收方:解封装恢复出相同密钥
openssl pkeyutl -decap -inkey mlkem.key -in ct.bin -secret ss_recv.bin
cmp -s ss_sender.bin ss_recv.bin && echo "shared secrets MATCH"
ciphertext: 1088 bytes,  shared secret: 32 bytes
shared secrets MATCH        # ML-KEM-768 尺寸符合 FIPS 203

2 ML-DSA-65 签名与验签

openssl genpkey -algorithm ML-DSA-65 -out mldsa.key
openssl pkey -in mldsa.key -pubout -out mldsa.pub
echo "hello pqc world" > msg.txt
openssl pkeyutl -sign   -inkey mldsa.key -rawin -in msg.txt -out msg.sig
openssl pkeyutl -verify -pubin -inkey mldsa.pub -rawin -in msg.txt -sigfile msg.sig
3309 byte signature        # ML-DSA-65 符合 FIPS 204
Signature Verified Successfully

3 SLH-DSA 保守的大签名方案

openssl genpkey -algorithm SLH-DSA-SHA2-128s -out slh.key
echo hi > m.txt
openssl pkeyutl -sign -inkey slh.key -rawin -in m.txt -out slh.sig
wc -c slh.sig
7856 slh.sig               # SLH-DSA-128s 签名符合 FIPS 205——在该参数集下约为 ML-DSA-65 的 2.4 倍;其他 SLH-DSA 参数集可大得多,
                           # 且签名慢得多。适用于最看重「仅依赖哈希」假设的场景。

4 真实世界中的混合 TLS 1.3

强制使用后量子混合组,并确认真实服务器会协商它(Cloudflare 已支持):

echo | openssl s_client -groups X25519MLKEM768 -connect cloudflare.com:443 \
  -servername cloudflare.com 2>/dev/null | grep -iE 'Negotiated TLS1.3 group|Protocol'
Negotiated TLS1.3 group: X25519MLKEM768
Protocol: TLSv1.3

真实公网端点的支持状态会随服务端配置和中间设备变化;若协商失败,不一定是 OpenSSL 不支持,也可能是该站点暂未提供该组或路径上存在降级/拦截设备。

盘点自有端点请用资产发现页的 scan-tls.sh

5 自签名 ML-DSA 证书

openssl req -x509 -new -newkey ML-DSA-65 -keyout ca.key -out ca.crt \
  -days 365 -nodes -subj "/CN=pqc-demo"
openssl x509 -in ca.crt -noout -text | grep -i 'Signature Algorithm' | head -1
Signature Algorithm: ML-DSA-65

这张证书适合本地实验;大量客户端、代理、负载均衡器和证书处理工具仍可能无法接受 ML-DSA 证书链。生产前必须端到端测试。

6 liboqs Python 算法级 API

若要遍历完整算法集(含 HQC、Falcon、McEliece),Open Quantum Safe 的绑定很方便。liboqs-python 的安装方式随平台和版本变化;若 pip 安装失败,应以当前 liboqs-python README 为准。部分环境需要先安装 CMake、编译器和 liboqs C 库。下面的 pip 命令只是其中一种可能的安装路径,仅作示意。

pip install liboqs-python
import oqs

with oqs.KeyEncapsulation("ML-KEM-768") as kem:
    pk = kem.generate_keypair()
    ct, ss_sender = kem.encap_secret(pk)
    ss_recv = kem.decap_secret(ct)
    assert ss_sender == ss_recv

with oqs.Signature("ML-DSA-65") as sig:
    pk = sig.generate_keypair()
    s = sig.sign(b"hello pqc world")
    assert sig.verify(b"hello pqc world", s, pk)

print(oqs.get_enabled_kem_mechanisms()[:5])
print(oqs.get_enabled_sig_mechanisms()[:5])

标准与参考

仅供实验
这些是学习用配方——请在临时目录生成一次性密钥。生产环境应使用经验证的密码模块 常量时间实现与硬件密钥保护 (见侧信道防护)。
⚑ Report an error⚑ 纠错与校正