Java 24原生支持抗量子密码:ML-KEM与ML-DSA实践
1. 概述
ML-KEM(基于模格的关键封装机制)是一种后量子密码学密钥交换算法,允许双方安全地建立共享密钥,即使面对未来量子计算机的攻击也能保持安全。而 ML-DSA(基于模格的数字签名算法)是一种后量子数字签名算法,能够提供安全的身份验证和消息完整性保护,抵抗量子计算机的攻击。
在 Java 24 之前,ML-KEM 和 ML-DSA 并不属于标准 JDK(Java 开发工具包)的一部分。我们需要使用 Bounty Castle 等外部库来执行密码学操作。
从 Java 24 开始,这两种算法都已纳入原生 Java API。原生 Java API 中的 ML-KEM 和 ML-DSA 实现分别由 JEP 496 和 JEP 497 提出。
在本教程中,我们将讨论如何在 Java 中使用抗量子的 ML-KEM 和 ML-DSA。
2. 抗量子 ML-KEM
关键封装机制 允许两方(即发送方和接收方)在不安全的网络上安全地交换共享密钥。该机制包括以下步骤:
- 接收方创建公钥/私钥对
- 发送方使用公钥创建加密的共享密钥
- 接收方使用私钥解密加密的共享密钥
因此,双方获得相同的密钥。该共享密钥可用于对称密钥算法,如 AES(高级加密标准)和 Blowfish。
ML-KEM 基于 M-LWE(模学习带误差)问题,该问题在计算上非常困难。M-LWE 问题的困难性进一步依赖于模格中某些计算问题的困难性。因此,ML-KEM 能够抵抗经典和量子攻击。
2.1. 生成密钥对
让我们首先在接收方生成一个 ML-KEM 密钥对:
首先,我们获取一个用于 ML-KEM 算法的 KeyPairGenerator 对象。然后,我们选择 ML_KEM_768 来设置即将生成的密钥的安全强度。JEP 496 增加了对 ML-KEM-512、ML-KEM-768 和 ML-KEM-1024 的支持。实际上,ML_KEM_768 是默认选项,即如果我们不通过 initialize() 方法初始化 KeyPairGenerator 对象,安全级别仍然是 ML_KEM_768。
也可以直接将安全强度传递给 getInstance(),这种情况下也无需调用 initialize() 方法:
receiverKeyPair 是 KeyPair 类型,包含一个公钥和一个私钥。我们可以分别使用 getPublic() 和 getPrivate() 成员方法访问这些密钥。接收方可以安全地将公钥分享给发送方,但私钥必须保密。
2.2. 生成并封装共享密钥
一旦发送方收到接收方的公钥,它就会生成并封装一个共享密钥,该密钥将被发送方和接收方共同使用:
senderSharedSecret 是实际用于通信的密钥。但发送方并不直接将其发送给接收方,而是通过使用接收方的公钥封装(加密)它来发送。ciphertext 对应于 senderSharedSecret 的加密版本。
2.3. 解封装共享密钥
接收方收到加密密钥后,使用私钥解封装该密钥:
实际的密钥 receiverSharedSecret 通过解封装接收到的 ciphertext 提取得到。因此,双方获得相同的共享密钥,用于对称加密和解密。我们可以通过以下方式检查双方使用的是相同密钥:
比较结果为 true。因此,我们实现了在不安全网络上的安全通信。此外,该方法能够抵抗量子攻击,而 RSA 或 Diffie-Hellman 则无法做到这一点。
3. 抗量子 ML-DSA
数字签名算法 是一种用于验证数字消息或文档真实性和完整性的密码学方法。该算法包括以下步骤:
- 签名者(发送方)创建公钥/私钥对
- 签名者使用消息和私钥创建签名
- 验证者(接收方)使用收到的消息和公钥验证签名
与 ML-KEM 类似,ML-DSA 也基于 M-LWE 问题和另一个计算上困难的问题——M-SIS(模短整数解问题)。因此,ML-DSA 能够抵抗经典和量子攻击。
3.1. 生成密钥对
让我们首先在签名方生成一个 ML-DSA 密钥对:
首先,我们获取一个用于 ML-DSA 算法的 KeyPairGenerator 对象。然后,我们选择 ML_DSA_65 来设置即将生成的密钥的安全强度。实际上,ML_DSA_65 是默认选项。JEP 497 增加了对 ML-DSA-44、ML-DSA-65 和 ML-DSA-87 的支持。
与 ML-KEM 类似,也可以直接将安全强度传递给 getInstance()。
3.2. 签名
然后,作为签名者,我们使用私钥对待发送的消息进行签名:
首先,我们获取一个实现 ML-DSA 算法的 Signature 对象。然后,我们使用 initSign() 方法将该对象初始化为签名模式,并将私钥传递给该方法。接着,我们使用 update() 方法将消息馈送到 Signature 对象。最后,我们使用 Signature 对象的 sign() 方法创建数字签名。
3.3. 验证
然后,作为验证者,收到消息和签名后,我们使用公钥验证签名:
这次,我们使用 initVerify() 方法将 Signature 对象初始化为验证模式。然后,我们使用 update() 方法将签名方发来的消息馈送到 Signature 对象。最后,我们使用 Signature 对象的 verify() 方法验证签名。如果返回 true,则表示验证成功,签名是用对应的私钥创建的。
4. 总结
在本文中,我们讨论了如何在 Java 中使用抗量子的 ML-KEM 和 ML-DSA。首先,我们了解到这些算法在 Java 24 之前并不存在于标准 JDK 中。
然后,我们看到了这些算法在 Java 中的用法。我们使用 ML-KEM 模拟了接收方和发送方之间的密钥交换。类似地,我们使用 ML-DSA 模拟了双方之间的消息签名和签名验证。从示例中可以看出,Java 24 为 KeyPairGenerator 和 KEM 类提供了 ML-KEM 实现,并为 KeyPairGenerator 和 Signature 类提供了 ML-DSA 实现。
与往常一样,示例的完整源代码可在 GitHub 上获取。