找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5970

积分

0

好友

762

主题
发表于 前天 23:07 | 查看: 0| 回复: 0

当美国国家标准与技术研究院(NIST)于 2024 年 8 月正式敲定 FIPS 203 和 FIPS 204 规范后,受监管行业里的大多数工程团队都在问同一个问题:“到底从哪里入手?”表面上的答案是“切换到 PQC TLS”,但现实是,各云服务提供商仍在部署这项技术,大部分团队还没法立即启用。

更紧迫的是,实际风险已经浮出水面——攻击者正在存储加密的服务间流量,就等量子硬件成熟后再解密,也就是所谓的“先收割,后解密”(HNDL)。

本文以一个基于 Spring Boot 构建的标准零售银行微服务平台为例展开。这个平台包含一个交易服务,既要把支付指令发送到 Core Banking Service,又要把客户的个人身份信息(PII)和 KYC 数据存进 PostgreSQL。同时,平台还会把贷款协议、开户文件归档到 Amazon S3,把 OAuth2 服务账户令牌集成到 SWIFT 和 ACH 连接器的监管报告管道中。这种拓扑结构在中大型银行里相当典型。真正的问题是,“量子安全”对这些组件分别意味着什么?组件不同,答案也不同。

文章使用一个名为 PqcStarterLib 的 Spring Boot PQC 库——它把 Bouncy Castle PQC 提供程序封装在三个自动配置的 Bean 中——针对上述拓扑结构讨论四种具体模式。这些模式包括:银行内部服务之间的有效载荷加密;Jakarta Persistence 在写入数据库前对 PII 和 KYC 进行字段级加密;用 Dilithium 对贷款协议和审计记录做长期文档签名;以及为 Core Banking 和监管报告管道中的服务账户签发量子安全的 OAuth2 令牌。每种模式都配有可运行的 Spring Boot 代码,也会诚实说明哪些地方还不能直接上生产。

零售银行之所以是 HNDL 策略的绝佳目标,是因为数据保质期极长。今天被盗的客户社会安全号码(SSN),到了 2035 年仍然有价值。一份贷款协议,如果 RSA 签名十年后可能被伪造,那将构成无法追溯补救的法律责任。本文描述的攻击模式,正是围绕这个现实来设计的。

威胁浅析

RSA 和 ECDSA 之所以有效,是因为对经典计算机来说,大数因式分解和离散对数问题都足够困难。而运行肖尔算法的量子计算机,可以在多项式时间内解决这两个问题。目前 IBM、谷歌以及多家国家实验室都已经拥有可运行的量子处理器,但还没有一台达到破解 RSA-2048 所需的规模。多数专家判断,这个临界点会在 2030 年到 2035 年之间出现。

最迫在眉睫的问题是 HNDL。攻击者现在就在拦截并存储加密的 TLS 流量,把建立 TLS 会话的 RSA 密钥交换过程和密文一并记录下来。一旦具备能力的量子计算机出现,他们就会回溯解密。对零售银行来说,受影响的范围包括跨服务流转的客户 KYC 数据、交易记录、银行间结算信息,以及任何需要长期保密的网络传输文件。

另一个不能拖延的方面是长期有效的签名文件。如果今天用 RSA 签了一份贷款协议,而这份协议在 2036 年仍然需要具备法律效力,那就成了一个事后无法解决的问题。根据监管辖区不同,银行会把贷款协议、开户合同和审计记录存档七到三十年不等。已经归档的文件,无法追溯性重新签名。

短期有效的数据风险相对低。一个 15 分钟就过期的客户会话令牌,就算用 RSA 签名,通常问题不大,因为还没来得及被破解就已经没价值了。但 Core Banking 系统集成、欺诈检测管道和 SWIFT 连接器使用的 OAuth2 服务账户令牌,有效期往往长达数月,性质就完全不同了。这些正是 HNDL 攻击的重点目标。

PqcStarterLib 向 Spring Boot 添加了什么

PqcStarterLib 基于 FIPS 203 和 FIPS 204 的 Bouncy Castle 实现构建,提供了三个在启动时自动配置的 Spring Bean:

  • PqcEncryptionService 提供混合加密能力:先用 Kyber KEM 建立一次性共享密钥,再用 AES-256-GCM 加密实际有效载荷。服务间消息正文或数据库字段需要保持机密性时,就用它。
  • PqcSignatureService 通过 CRYSTALS-Dilithium 提供签名和验证功能。需要证明内容未被篡改的贷款协议、KYC 文件、审计记录、构建工件和 OAuth2 令牌,都可以用它。
  • PqcKeyPairGenerator 作为自动配置的 Spring Bean,用于生成 Kyber 和 Dilithium 密钥对。

集成只需要三行代码:

@Autowired PqcEncryptionService pqc;
@Autowired PqcSignatureService  pqcSig;
byte[] ciphertext = pqc.encrypt(data, recipientPublicKey).toBytes();
byte[] signature  = pqcSig.sign(document, myPrivateKey);
boolean ok        = pqcSig.verify(document, signature, myPublicKey);

关于依赖关系的说明

Bouncy Castle(bcprov-jdk18on)向下兼容 JDK 11,这覆盖了 LTS 周期内的大多数银行系统。如果你用的是 JDK 24 及以上版本,SunJCE 提供程序已经通过 JEP 496 和 JEP 497 原生支持 ML-KEM 和 ML-DSA,不需要任何外部库:

// 仅支持 JDK 24+ 版本,不需要 Bouncy Castle 
2
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM-768");
3
KeyPair kyberPair = kpg.generateKeyPair();
4
5
Signature signer = Signature.getInstance("ML-DSA-65");
6
signer.initSign(dilithiumPrivateKey);
7
signer.update(message);
8
byte[] sig = signer.sign();

Bouncy Castle 的参数设置更灵活,而且支持 JDK 11 和 JDK 17。原生提供程序没有依赖,也是 NIST 标准 Java 工具集目前的发展方向。如果一家银行本来就计划升级到 JDK 24,那么选原生方案是值得的。

用例 1:服务间有效载荷加密

情况说明

交易服务通过 HTTP 把客户的支付指令发送到 Core Banking Service。虽然 TLS 保护了传输过程,却防不住 HNDL 攻击。如果攻击者今天记录了 RSA 密钥交换,日后就可以用量子计算机解密整个会话。在典型银行基础设施里,TLS 连接通常在 API 网关、服务网格和内部负载均衡器处就终止了,所以交易服务与 Core Banking Service 之间的实际微服务跳转,在网络边界内部往往本来就是未加密的。

模式

发送前用 Kyber+AES-256-GCM 对 HTTP 正文加密,这个加密过程与 TLS 相互独立。Core Banking Service 接收一个 PqcEncryptedPayload 记录,再用自己的 Kyber 私钥解密。即便 TLS 被完全移除,支付指令仍然受保护。

// 交易服务:sender
@Autowired PqcEncryptionService pqc;
PaymentInstruction instruction = buildInstruction(transfer);
byte[] payload = objectMapper.writeValueAsBytes(instruction);
PqcEncryptedPayload encrypted = pqc.encrypt(
   payload,
   coreBankingPublicKey   // Kyber-768 公钥
);
restTemplate.postForObject("/core-banking/process", encrypted, Void.class);

// Core Banking Service:receiver
@PostMapping("/core-banking/process")
public ResponseEntity<?> process(@RequestBody PqcEncryptedPayload body) {
   byte[] plaintext = pqc.decrypt(body, coreBankingPrivateKey);
   PaymentInstruction instruction =
       objectMapper.readValue(plaintext, PaymentInstruction.class);
   // 记入分类账……
   return ResponseEntity.ok().build();
}

这就是 NIST 在迁移指南中描述的双层架构模式:TLS 应对经典攻击者,Kyber 应对量子攻击者,两者必须分别破解。你不需要改动 TLS 配置、API 网关配置或服务网格。

关注要点

Kyber-768 的公钥大小约为 1184 字节,放在 HTTP 请求正文里没问题,但别塞进请求头。Dilithium-3 的签名大小约为 3300 字节,如果要序列化成 JWT 声明或 ISO 20022 消息信封,就得格外注意。

用例 2:PII 和 KYC 字段级加密

情况说明

客户的 SSN、税务识别号码、出生日期字段以及 KYC 文件参考信息,通常存储在 PostgreSQL 中。静态数据可能经过了 AES 加密,但密钥往往放在环境变量里,或者启动时从配置服务器获取。一旦发生 SQL 注入导致的数据库导出、备份配置错误或内部人员泄密,所有信息就都暴露了。在零售银行业,单次 SSN 泄露就足以构成对 GDPR、CCPA、GLBA 以及各州金融数据保护法的监管违规。

模式

在用 Jakarta Persistence 持久化实体之前,用 PqcEncryptionService 对每个敏感字段加密。数据库列中存储的是 Base64 编码的密文二进制块,明文值用 @Transient 注解标记,绝不会写入数据库。

// 保存
public CustomerProfile saveProfile(CustomerDto dto) {
   CustomerProfile profile = new CustomerProfile();
   profile.setFullName(dto.getFullName());
   byte[] ssnCipher = pqc.encryptToBytes(
       dto.getSsn().getBytes(UTF_8),
       masterKeyPair.getPublic()
   );
   profile.setEncryptedSsn(
       Base64.getEncoder().encodeToString(ssnCipher)
   );
   return repo.save(profile);
}

// 检索
public String getSsn(Long customerId) {
   CustomerProfile p = repo.findById(customerId).orElseThrow();
   byte[] blob = Base64.getDecoder().decode(p.getEncryptedSsn());
   return new String(pqc.decrypt(blob, masterKeyPair.getPrivate()), UTF_8);
}

阻碍字段级加密投入生产的关键管理问题

上面这段代码在开发环境跑得通,但 masterKeyPair.getPrivate() 返回的是存放在 JVM 堆里的 Kyber 私钥。在银行业务场景中,这种做法会引发三个让安全审计无法通过的问题:

  • 如果服务器重启时没有持久化的密钥存储,所有加密的客户记录就再也读不出来了。
  • 从任何正在运行的实例中获取堆转储,都会泄露数据库中每个 SSN 和税务识别号的主私钥。
  • 这个方案没有实现密钥轮换,而 GLBA、PCI-DSS 和 SOC 2 标准对此都有明确要求。

解决办法是让所有 Kyber 密钥操作都通过 AWS KMS 或 HashiCorp Vault 进行,应用程序本身不持有原始私钥。每次解密都是经过日志记录且可审计的 KMS 调用,密钥按计划轮换。这种模式虽然已被广泛理解,但它和加密本身属于不同的工程工作流,必须在任何加密字段进入生产环境之前就位。没有这个机制,你拥有的只是一个可运行的概念验证,而不是可部署的系统。

用例 3:贷款协议的电子签名与审计追踪

情况说明

零售银行每天都在签署贷款协议、开户合同和监管审计记录,这些文件要存档七到三十年。一份今天用 RSA 签署的贷款协议,签名到 2035 年左右仍可能被伪造。如果一家银行到 2034 年才意识到这个风险,就无法追溯性地重新签署过去十年的归档文件了。这既是法律风险,也是 DORA 和 OCC 指南等监管框架下的合规问题。

模式

在创建时就使用 Dilithium 对所有长期有效文档签名。Dilithium 是基于晶格的算法,其安全性假设不受肖尔算法影响。今天生成的签名,到 2045 年仍具备密码学有效性。

@Entity
public class CustomerProfile {
   private String fullName;
   private String accountNumber;
   @Column(name = "ssn_encrypted")
   private String encryptedSsn;
   @Column(name = "tax_id_encrypted")
   private String encryptedTaxId;
   @Transient   // 永远不持久化
   private String ssn;
   @Transient   // 永远不持久化
   private String taxId;
}

// 文档创建时
public SignedDocument signLoanAgreement(
       byte[] agreementPdf,
       PrivateKey signerKey,
       String officerId) {
   byte[] signature = pqcSig.sign(agreementPdf, signerKey);
   return SignedDocument.builder()
       .document(agreementPdf)
       .signature(Base64.getEncoder().encodeToString(signature))
       .algorithm("DILITHIUM3")
       .signedAt(Instant.now())
       .signerId(officerId)
       .documentType("LOAN_AGREEMENT")
       .build();
}

// 验证:同一段代码在 2026 年、2031 年或 2045 年均可正常运行
public VerificationResult verify(SignedDocument doc) {
   byte[] sig = Base64.getDecoder().decode(doc.getSignature());
   PublicKey pub = keyRegistry.getPublicKey(doc.getSignerId());
   boolean valid = pqcSig.verify(doc.getDocument(), sig, pub);
   return VerificationResult.builder()
       .valid(valid)
       .signerId(doc.getSignerId())
       .signedAt(doc.getSignedAt())
       .quantumSafe(true)
       .build();
}

同样的模式也适用于 CI/CD 的构建产物签名。部署到银行基础设施中的每个 JAR 文件,都应该由构建管道用 Dilithium 签名。执行任何 kubectl apply 命令之前,部署门会先验证签名,不匹配就直接因 SecurityException 中止。这样能捕获构建产物注册表与生产环境之间的供应链注入攻击——这类攻击发生在注册表边界内部,标准 TLS 检测不到。

在本文的四种模式中,文档和工件签名是最接近生产就绪的。它不依赖 KMS 部署,签名密钥可以走现有密钥基础设施管理,而且紧迫性论据足够具体,容易获得法律和合规团队的支持。

用例 4:用于 Core Banking Services 的量子安全 OAuth2 令牌

零售银行业的授权层实施,比典型微服务架构更需要加紧推进,原因如下。

短效客户会话令牌(RS256,有效期 15 分钟)优先级确实低,还没被破解就已经失效了。但银行业有两种令牌情况完全不同:Core Banking 系统集成、欺诈检测引擎、SWIFT 网关连接器以及 ACH 报告管道使用的 OAuth2 服务账户令牌。这些令牌通常有效期长达数月甚至数年,并且存储在 CI/CD 密钥库和基础设施自动化工具中。任何承载合规监管相关声明的令牌,在合规层面都具有长期重要性。

这些正是 HNDL 攻击收集的凭证。如果今天有攻击者窃取并存储了你 SWIFT 连接器的服务账户凭证,到 2031 年再解密,就能利用它以认证身份访问 Core Banking API 并进行重放攻击。

模式

把 RS256 替换成 DILITHIUM3,用于服务账户令牌签名。JWT 结构完全相同,只是 alg 头和签名调用方式变了。

// 身份验证服务器:服务账户令牌签发
public String issueServiceToken(ServiceAccount account) {
   String header = base64url(
       "{\"alg\":\"DILITHIUM3\",\"typ\":\"JWT\"}"
   );
   String payload = base64url(String.format(
       "{\"sub\":\"%s\",\"scope\":\"%s\",\"iat\":%d,\"exp\":%d}",
       account.getClientId(),
       account.getScopes(),
       now(),
       now() + 86400
   ));
   String signingInput = header + "." + payload;
   byte[] sig = pqcSig.sign(
       signingInput.getBytes(UTF_8),
       authServerPrivateKey
   );
   return signingInput + "." + base64url(sig);
}

// API 网关:令牌验证
public Claims validateServiceToken(String jwt) {
   String[] parts = jwt.split("\\.");
   String signingInput = parts[0] + "." + parts[1];
   byte[] signature = base64urlDecode(parts[2]);
   boolean valid = pqcSig.verify(
       signingInput.getBytes(UTF_8),
       signature,
       authServerPublicKey
   );
   if (!valid) throw new InvalidTokenException(
       "Dilithium token verification failed"
   );
   return parsePayload(parts[1]);
}

需要做好哪些准备

Dilithium-3 的签名大小约为 3300 字节,而 RS256 只有 256 字节。携带 Dilithium-3 签名的 JWT 会大很多。如果你的 API 网关有请求大小限制,或者 SWIFT 连接器对消息信封大小有严格要求,部署前就要先检查这些限制。在开源生态中,OAuth2 流程与 Spring Security 的全面集成仍在进行中。因此,这个用例只能列入近期工作计划,而不是当前即可发布的方案。

如何安排工作顺序

大多数团队不应该试图一次性做完所有事情。在银行业背景下,风险排序大致如下:长期文档签名最紧迫,其次是服务间高敏感数据的有效载荷加密,最后才是服务账户令牌和全面的 TLS 层迁移。

请立即进行以下更改:

  • 为 Kyber 密钥管理部署 AWS KMS 或 HashiCorp Vault。不做这一步,其他任何方案都无法确保生产安全。
  • 针对传输最敏感数据的两到三个服务间数据流(涉及客户 PII、KYC 记录或支付指令的),添加 Kyber+AES-256-GCM 有效载荷加密。
  • 把贷款协议和监管文件的签署迁移到 Dilithium。这一项最紧迫,因为归档文件无法追溯修正,而且不需要先部署 KMS。

在未来六到十八个月内实施以下变更:

  • 在内部服务网格的其余部分全面推行有效载荷加密。
  • Jakarta Persistence/Hibernate ORM 字段加密辅助函数,支持从 KMS 生成每条记录专属密钥。
  • 在内部银行基础设施中,对 CI/CD 构建产物采用 Dilithium 签名。
  • 一旦 JEP 527 在即将发布的 JDK 27 中落地,且云服务提供商支持新密码套件,就在 TLS 1.3 中启用 PQC。

关键管理机制稳固之后,为服务账户的 Dilithium OAuth2 令牌签名添加 Spring Security 集成。然后在 API 网关层为 Core Banking 系统和监管管道端点添加支持 PQC 的令牌验证机制。

需要避免的是把短效客户会话令牌作为起点。它们虽然是最常见的授权模式,但也是这份清单里风险最低的一项。应该从有效期最长、监管风险最大的项开始。

小结

零售银行业 Spring Boot 应用集群的 PQC 迁移不必一次性完成。NIST 标准已经确定,JDK 24 提供了原生支持,Bouncy Castle 能满足仍在使用 JDK 11 或 JDK 17 的团队。目前最紧迫的工作——内部服务流量的有效载荷加密、长期文档的 Dilithium 签名以及 PII 的字段级加密——只需要通过库集成和一次专注的冲刺就能全部实现。

难点其实不在密码学本身,而在底层的密钥管理。没有部署 KMS 或 HashiCorp Vault,字段级加密的概念验证虽然能跑起来,但过不了银行的安全审查。先把基础打好,其余工作自然会循序渐进。

原文链接: https://www.infoq.com/articles/pqc-in-spring-boot/




上一篇:普林斯顿王梦迪:大模型的长尾困境与AI科学发现的可验证性
下一篇:RTX 4060 跑 35B MoE 模型每秒 39 Token,伯克利 MIT 开源 FreeToken
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-13 17:38 , Processed in 3.293945 second(s), 45 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表