后量子密码(PQC)不再只是密码学团队的远期课题。对于 Spring Boot 服务,真正可执行的路径不是一次性替换整个加密栈,而是从四类边界清晰的场景开始:服务间负载加密、数据库字段保护、长期有效文件签名,以及把服务令牌从 RS256 迁移出去。
更现实的原因是“先窃取、后解密”(Harvest Now, Decrypt Later)已经改变了时间表:攻击者可能现在收集加密流量和归档数据,等量子计算能力成熟后再解密。因此,涉及长期保密数据和多年有效签名的系统,应该现在开始盘点,而不是等算法和基础设施完全成熟后再行动。
四个适合 Spring Boot 的切入点
1. 服务间负载:在消息体外再加一层加密
微服务通常依赖 mTLS 保护传输链路,但 mTLS 主要解决“传输中”的保护问题。消息进入网关、队列、日志系统或重试存储后,仍可能暴露在中间环节。
一种可以快速试点的模式是使用混合加密:用对称密钥加密实际负载,再用支持后量子算法的密钥封装机制保护这个对称密钥。这样既避免直接用公钥算法处理大消息,也方便未来更换算法。
可以先把协议设计成带版本号的信封格式:
{
"version": 1,
"kem": "pqc-kem-v1",
"keyId": "payments-prod-2025-01",
"wrappedKey": "base64url(...) ",
"nonce": "base64url(...) ",
"ciphertext": "base64url(...) ",
"aad": "service-a|service-b|order-created"
}
这里的 kem 只是协议字段示例,实际算法名称和 JCA Provider 取决于你选择的密码库及其支持情况。不要把某个实验性 Provider 的算法名硬编码进所有业务代码;把加解密能力封装在一个 CryptoEnvelopeService 中,业务层只处理明文和信封对象。
2. 数据库字段:优先保护高价值、长生命周期数据
不建议一开始就给整张表加密。可以从身份证号、支付账户、医疗记录、客户密钥材料等字段开始,建立字段级加密策略:
- 数据库只保存密文、算法版本、密钥 ID 和必要的 nonce/tag。
- 应用读取时通过密钥管理服务解封密钥,而不是从环境变量读取主密钥。
- 搜索需求单独设计,例如保存经过密钥派生的盲索引,而不是对密文做模糊查询。
- 密钥轮换时保留旧版本解密能力,并通过后台任务逐步重加密。
一个 Spring Boot 配置可以先把密钥引用放到 Vault 或 KMS,而不是直接放入 application.yml:
spring:
application:
name: customer-service
crypto:
provider: pqc-provider
key-reference: vault:secret/data/customer-service/field-encryption
key-version: v1
envelope-format: v1
management:
endpoints:
web:
exposure:
include: health,info
这段配置本身不会自动提供 PQC。它表达的是一个重要边界:应用配置保存“引用”和“策略”,真正的密钥材料交给 Vault 或云 KMS。生产环境还应限制应用身份只能读取所需密钥,并记录每次解封调用。
3. 文档签名:为“十年后仍要验证”设计
合同、合规报告、医疗文档和软件供应链元数据,往往需要在多年后证明“当时是谁签的、内容是否被改过”。这类场景比普通 API 请求更适合优先评估后量子签名,或者采用经典签名与后量子签名并行的混合方案。
签名数据不要只包含文件内容,还应包含明确的上下文:文档类型、业务 ID、签名时间、算法版本、签名者身份和证书链引用。验证器应支持算法版本并行存在,避免未来更换算法时无法读取历史文件。
一个最小的服务端接口可以这样设计:
curl -X POST 'http://localhost:8080/api/documents/123/sign' \
-H 'Authorization: Bearer dev-token' \
-H 'Content-Type: application/json' \
-d '{
"signatureProfile": "long-lived-v1",
"includeClassicalSignature": true
}'
在生产实现中,签名私钥应由 KMS、HSM 或 Vault 托管,Spring Boot 服务只获得执行签名操作的权限。开发环境可以使用测试密钥,但必须让密钥来源和签名配置可替换,否则试点代码很容易直接进入生产。
4. 服务令牌:不要把 RS256 当成永久默认值
很多内部服务使用 JWT 和 RS256。迁移时,最容易犯的错误是一次性更换所有服务的算法,导致旧客户端、缓存、网关和轮换逻辑同时失效。
更稳妥的做法是建立算法迁移窗口:
- JWKS 或令牌验证器同时识别旧算法和新算法。
- 新签发令牌优先使用新的签名配置。
- 给令牌设置较短的过渡期,并记录旧算法令牌的使用来源。
- 当旧客户端归零后,再禁用 RS256。
令牌头部应携带清晰的 kid 和版本信息,验证端必须执行算法白名单,不能根据令牌自身的 alg 字段盲目信任。下面是一个用于本地联调的请求示例:
curl -X POST 'http://localhost:8080/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=inventory-service' \
--data-urlencode 'client_secret=local-only-secret' \
--data-urlencode 'scope=inventory.read'
这个命令只演示客户端如何获取令牌,并不规定具体的后量子签名算法。JWT 生态、网关和语言运行时对后量子签名的支持程度并不一致,实践中应先确认验证库、JWKS 格式、令牌大小和代理限制。
一个可交付的 Spring Boot 试点边界
可以把第一个 Sprint 的目标控制在一个服务和一个数据流内:
producer -> envelope encrypt -> message broker -> envelope decrypt -> consumer
|
+-> audit event without plaintext
建议交付以下内容:
- 一个版本化的密文信封格式。
- 一个可替换的
CryptoEnvelopeService接口。 - 一套测试向量,覆盖成功解密、错误 key ID、篡改 ciphertext、过期版本和重复 nonce。
- KMS 或 Vault 的开发、预生产和生产路径。
- 指标:解封失败率、密钥调用延迟、算法版本分布和旧算法使用量。
- 明确的回滚策略:回滚应用版本不等于回滚密钥,也不应删除仍需解密的旧密钥版本。
可以这样定义业务层接口,让算法实现与 Spring 服务解耦:
public interface CryptoEnvelopeService {
EncryptedEnvelope encrypt(byte[] plaintext, String aad, String keyReference);
byte[] decrypt(EncryptedEnvelope envelope, String aad);
}
public record EncryptedEnvelope(
int version,
String kem,
String keyId,
String wrappedKey,
String nonce,
String ciphertext
) {}
这是接口级示例,不是完整的密码实现。真正的实现必须使用经过审查的密码库,正确处理随机数、认证标签、密钥轮换和异常信息;不要自行实现 KEM、签名算法或 AEAD。
生产前必须补齐的基础设施
PQC 算法只是拼图的一部分。没有 KMS、Vault 或 HSM,密钥仍可能出现在镜像层、日志、堆转储和 CI 输出中,系统不能算生产安全。
至少要检查:
- 密钥是否由集中式服务生成、存储和轮换。
- Spring Boot 工作负载是否使用短期身份访问密钥。
- 审计日志是否记录密钥使用者、用途和结果,但不记录明文及密钥材料。
- 备份、消息队列、日志归档和灾备副本是否覆盖相同的加密策略。
- 算法、Provider、证书和序列化格式是否有明确的升级路径。
- 性能测试是否包含更大的公钥、签名和令牌尺寸对网关、数据库列及消息队列的影响。
采用建议:先按数据寿命排序
并不是每个低价值、短生命周期的缓存字段都需要立即迁移。可以用三个问题排优先级:数据是否需要保密很多年?是否正在被归档或跨系统复制?签名是否需要在多年后继续验证?只要答案为“是”,就值得进入 PQC 迁移清单。
实际落地时,优先选一个边界明确的服务,采用版本化信封、集中式密钥管理和可观测的双轨验证。先让协议能演进,再扩大覆盖范围。这样既能应对 Harvest Now, Decrypt Later 的现实风险,也不会因为一次性替换全 fleet 而制造新的停机和兼容性风险。