JEP 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism | 量子抗性基于模块格的密钥封装机制
概述
通过提供基于模块格的密钥封装机制(ML-KEM)的实现来增强 Java 应用程序的安全性。密钥封装机制(KEMs)用于使用公钥密码术在不安全的通信信道上保护对称密钥的安全。ML-KEM 设计为能够抵御未来量子计算机的攻击。它已被美国国家标准与技术研究院(NIST)在 FIPS 203 中标准化。
目标
- 提供
KeyPairGenerator
、KEM
和KeyFactory
API 的 ML-KEM 实现,并支持 FIPS 203 中标准化的参数集 ML-KEM-512、ML-KEM-768 和 ML-KEM-1024。
非目标
- 不旨在实现从 ML-KEM 衍生但两者不可互操作的 Kyber 算法。
- 对于尚未存在必要标准的 Java 平台组件,不打算添加 ML-KEM 支持,特别是
javax.net.ssl
包中的传输层安全性(TLS)实现。一旦这些标准存在,我们将添加相应的支持。
动机
多年来,量子计算 领域一直在稳步发展。未来的大型量子计算机可以使用 Shor 算法,该算法能够因式分解整数并解决离散对数问题,从而威胁到广泛部署的基于公钥的算法的安全性,包括 Rivest-Shamir-Adleman(RSA)和 Diffie-Hellman。这些算法被 Java 平台用来进行 JAR 文件的数字签名以及通过传输层安全性(TLS)协议建立安全网络连接。传统超级计算机可能需要成千上万到百万年才能完成的攻击,量子计算机使用 Shor 算法仅需几小时即可完成。
为了应对这种威胁,密码学家发明了 抗量子 加密算法,这些算法不能被 Shor 算法击败。尽管大型量子计算机尚未存在,切换到抗量子算法已迫在眉睫,因为对手可以今天收集加密数据,存储起来,待此类计算机可用时再解密。
为了以抗量子的方式交换密钥,NIST 在 FIPS 203 中标准化了基于模块格的密钥封装机制(ML-KEM)。在美国,处理敏感信息的 政府计算机系统 在未来十年内必须升级为使用 ML-KEM。因此,Java 平台提供该算法的实现至关重要。
描述
如 JEP 452 所述,KEM 由三个函数组成:
- 密钥对生成函数 返回包含公钥和私钥的密钥对。
- 密钥封装函数,由发送者调用,接收接收者的公钥和加密选项;它返回一个秘密密钥 K 和一个 密钥封装消息。发送者将密钥封装消息发送给接收者。
- 密钥解封装函数,由接收者调用,接收接收者的私钥和接收到的密钥封装消息;它返回秘密密钥 K。
对于第一个函数,我们将提供 KeyPairGenerator
API 的实现,该实现生成 ML-KEM 密钥对。对于第二和第三个函数,我们将提供 KEM
API 的实现,该实现根据 ML-KEM 密钥对协商共享的秘密密钥。我们还将提供 KeyFactory
API 的实现,该实现将 ML-KEM 密钥与其编码相互转换。
在 Java Security Standard Algorithm Names Specification 中,我们将定义一个新的标准算法族名称“ML-KEM”,用于 KeyPairGenerator
、KEM
和 KeyFactory
API。
FIPS 203 指定了 ML-KEM 的三个参数集。按照安全强度递增和性能递减的顺序,它们分别命名为“ML-KEM-512”、“ML-KEM-768”和“ML-KEM-1024”。这些参数集名称也将作为 KeyPairGenerator
、KEM
和 KeyFactory
API 的标准算法名称定义,并进一步表示为新的 NamedParameterSpec
常量 ML_KEM_512、ML_KEM_768 和 ML_KEM_1024。
生成 ML-KEM 密钥对
您可以通过以下三种方式之一生成 ML-KEM 密钥对:
使用族名称实例化一个
KeyPairGenerator
并使用参数集名称初始化它:javaKeyPairGenerator g = KeyPairGenerator.getInstance("ML-KEM"); g.initialize(NamedParameterSpec.ML_KEM_512); KeyPair kp = g.generateKeyPair(); // ML-KEM-512 密钥对
如果您没有使用参数集初始化
KeyPairGenerator
,则实现将默认使用 ML-KEM-768:javaKeyPairGenerator g = KeyPairGenerator.getInstance("ML-KEM"); KeyPair kp = g.generateKeyPair(); // ML-KEM-768 密钥对
直接使用参数集名称实例化
KeyPairGenerator
:javaKeyPairGenerator g = KeyPairGenerator.getInstance("ML-KEM-1024"); KeyPair kp = g.generateKeyPair(); // ML-KEM-1024 密钥对
KeyPairGenerator
API 允许在初始化期间指定整数密钥大小,但这对于 ML-KEM 密钥对不支持,并且会抛出 InvalidParameterException
。
keytool
命令将支持生成 ML-KEM 密钥对和证书。例如,要生成 ML-KEM 密钥对并使用 EC 密钥签署证书:
$ keytool -keystore ks -storepass changeit -genkeypair -alias ec \
-keyalg ec -dname CN=ec -ext bc
$ keytool -keystore ks -storepass changeit -genkeypair -alias mlkem \
-keyalg ML-KEM -groupname ML-KEM-768 -dname CN=ML-KEM -signer ec
第一个命令创建一个 EC 密钥对;第二个命令创建一个 ML-KEM 密钥对和由 EC 密钥签名的证书。我们使用 EC 密钥签署证书,因为 ML-KEM 本身不是签名算法,因此不能用于签署包含 ML-KEM 公钥的证书。
参数集名称(ML-KEM-768)也可以直接与 -keyalg
选项一起提供:
$ keytool -keystore ks -storepass changeit -genkeypair -alias mlkem2 \
-keyalg ML-KEM-768 -dname CN=ML-KEM2 -signer ec
封装和解封装 ML-KEM 密钥
您可以使用 ML-KEM KEM
实现来协商共享秘密密钥。
例如,发送者可以调用封装函数以获取秘密密钥和密钥封装消息:
KEM ks = KEM.getInstance("ML-KEM");
KEM.Encapsulator enc = ks.newEncapsulator(publicKey);
KEM.Encapsulated encap = enc.encapsulate();
byte[] msg = encap.encapsulation(); // 发送此消息给接收者
SecretKey sks = encap.key();
然后,接收者可以调用解封装函数从发送者发送的密钥封装消息中恢复秘密密钥:
byte[] msg = ...; // 从发送者处收到
KEM kr = KEM.getInstance("ML-KEM");
KEM.Decapsulator dec = kr.newDecapsulator(privateKey);
SecretKey skr = dec.decapsulate(msg);
sks
和 skr
都包含相同的密钥材料,这些材料只有发送者和接收者知道。
如果使用族名称实例化了一个 KEM
对象,它接受家族中使用任何参数集的 ML-KEM 密钥。如果使用参数集名称实例化,则只接受使用该参数集的 ML-KEM 密钥;否则,newEncapsulator
和 newDecapsulator
方法将抛出 InvalidKeyException
。
编码和解码 ML-KEM 密钥
您可以使用 ML-KEM KeyFactory
实现将 ML-KEM 私钥转换为其 PKCS #8 编码,或将 ML-KEM 公钥转换为其 X.509 编码。
例如,将 ML-KEM 私钥转换为其 PKCS #8 编码,反之亦然:
KeyFactory f = KeyFactory.getInstance("ML-KEM");
PKCS8EncodedKeySpec p8spec = f.getKeySpec(kp.getPrivate(), PKCS8EncodedKeySpec.class);
PrivateKey sk2 = f.generatePrivate(p8spec);
类似地,将 ML-KEM 公钥转换为其 X.509 编码,反之亦然:
X509EncodedKeySpec x509spec = f.getKeySpec(kp.getPublic(), X509EncodedKeySpec.class);
PublicKey pk2 = f.generatePublic(x509spec);
只要其编码格式受支持,KeyFactory
实现还可以使用 translateKey
方法从另一个安全提供者翻译密钥。
由 ML-KEM KeyPairGenerator
或 KeyFactory
实现生成的 Key
对象的 getAlgorithm
方法总是返回族名称 "ML-KEM"
,无论 KeyPairGenerator
或 KeyFactory
是使用 "ML-KEM"
族名称还是其中一个参数集名称实例化的。ML-KEM 密钥的 getParams
方法返回一个匹配密钥参数集名称的 NamedParameterSpec
对象。
如果使用族名称实例化了 KeyFactory
对象,它将对家族中使用任何参数集的 ML-KEM 密钥进行编码或解码。如果使用参数集名称实例化,则只对使用该参数集的 ML-KEM 密钥进行编码或解码;否则,translateKey
方法将抛出 InvalidKeyException
,而 generatePrivate
、generatePublic
和 getKeySpec
方法将抛出 InvalidKeySpecException
。
ML-KEM KeyFactory
使用的编码在 IETF RFC 草案 中定义。我们将跟踪此草案中的更改,直到其发布为止。
替代方案
Open Quantum Safe 项目 为他们的
liboqs
C 库 提供了一个 JNI 包装器,该库实现了一系列抗量子算法,包括 Kyber 和 ML-KEM。如果 Open Quantum Safe 能够实现成为诸如 OpenSSL、BoringSSL、OpenSSH 和 Mozilla 等主要项目的首要抗量子密码实现的目标,那么它将通过广泛的测试和使用获得显著的性能提升和健壮性。与本机实现相比,ML-KEM 的 Java 实现提供的关键优势是直接集成到 JDK 中。这使得它在所有已移植 JDK 的平台上都立即可用。
测试
单元测试将确认实现是否符合
KeyGenerator
、KeyFactory
和KEM
API 的规范,包括无效输入参数、边界值和支持的操作等边缘情况。已知答案测试(KATs)将涵盖有效的加密操作(正面案例)以及无效操作或已知漏洞(负面案例),以确保全面验证。这些测试将不仅限于:
由 NIST 的 加密算法验证程序服务 生成的 KATs(此处 和 此处),
来自 Project Wycheproof 的 ML-KEM 测试,它们正在 开发中。
与其他供应商的实现(包括但不限于
liboqs
)进行互操作性测试,以确认我们的 ML-KEM 实现在与其他实现一起工作时表现良好。