跳到主要内容

JEP 496: 抗量子模块-基于格的密钥封装机制

QWen Max 中英对照 JEP 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism

概要

通过提供一种抗量子的基于模块格的密钥封装机制(ML-KEM)的实现来增强 Java 应用程序的安全性。密钥封装机制(KEMs)使用公钥加密技术在不安全的通信信道上保护对称密钥。ML-KEM 被设计为能够抵御未来的量子计算攻击。它已被美国国家标准与技术研究院(NIST)在FIPS 203中标准化。

目标

  • 提供 ML-KEM 实现的 KeyPairGeneratorKEMKeyFactory API,支持 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's algorithm 打败。尽管大规模量子计算机尚不存在,但切换到抗量子算法是迫在眉睫的,因为对手可以收集今天加密的数据,存储起来,并在这样的计算机出现后解密它。

为了以抗量子的方式交换密钥,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 安全标准算法名称规范 中,我们将为 KeyPairGeneratorKEMKeyFactory API 定义一个新的标准算法族名称 "ML-KEM"

FIPS 203 指定了 ML-KEM 的三个参数集。按照安全强度递增和性能递减的顺序,它们分别命名为 "ML-KEM-512""ML-KEM-768""ML-KEM-1024"。这些参数集名称也将被定义为 KeyPairGeneratorKEMKeyFactory API 的标准算法名称,并且还将由新的 NamedParameterSpec 常量 ML_KEM_512ML_KEM_768ML_KEM_1024 表示。

生成 ML-KEM 密钥对

您可以使用以下三种方法之一生成 ML-KEM 密钥对:

  • 使用家族名称实例化一个 KeyPairGenerator,并使用参数集名称进行初始化:

    KeyPairGenerator g = KeyPairGenerator.getInstance("ML-KEM");
    g.initialize(NamedParameterSpec.ML_KEM_512);
    KeyPair kp = g.generateKeyPair(); // 一个 ML-KEM-512 密钥对
  • 如果你没有使用参数集初始化 KeyPairGenerator,实现将默认使用 ML-KEM-768:

    KeyPairGenerator g = KeyPairGenerator.getInstance("ML-KEM");
    KeyPair kp = g.generateKeyPair(); // 一个 ML-KEM-768 密钥对
  • 直接使用参数集名称实例化一个 KeyPairGenerator

    KeyPairGenerator 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(); // send this to receiver
SecretKey sks = encap.key();

接收方然后可以调用解封装函数,从发送方发送的密钥封装消息中恢复出密钥:

A receiver can then call the decapsulation function to recover the secret key from the key encapsulation message sent by the sender:
markdown
byte[] msg = ...;                       // received from sender
KEM kr = KEM.getInstance("ML-KEM");
KEM.Decapsulator dec = kr.newDecapsulator(privateKey);
SecretKey skr = dec.decapsulate(msg);

sksskr 包含相同的密钥材料,这些材料只有发送方和接收方才知晓。

如果使用家族名称实例化一个 KEM 对象,它将接受该家族中任何参数集的 ML-KEM 密钥。如果使用参数集名称实例化,则它只接受使用该参数集的 ML-KEM 密钥;否则,newEncapsulatornewDecapsulator 方法将抛出 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 KeyPairGeneratorKeyFactory 实现生成的 Key 对象的 getAlgorithm 方法始终返回族名称 "ML-KEM",无论 KeyPairGeneratorKeyFactory 是用 "ML-KEM" 族名称还是其中一个参数集名称实例化的。ML-KEM 密钥的 getParams 方法返回一个与密钥的参数集名称匹配的 NamedParameterSpec 对象。

如果使用族名称实例化 KeyFactory 对象,它将编码或解码该族中使用任何参数集的 ML-KEM 密钥。如果使用参数集名称实例化,则它仅编码或解码使用该参数集的 ML-KEM 密钥;否则,translateKey 方法会抛出 InvalidKeyException,而 generatePrivategeneratePublicgetKeySpec 方法会抛出 InvalidKeySpecException

ML-KEM KeyFactory 使用的编码在 draft IETF RFC 中定义。我们将会跟踪该草案的变化,直到它被正式发布。

替代方案

  • Open Quantum Safe 项目 为他们的 liboqs C 语言库 提供了一个 JNI 封装,该库实现了一系列抗量子算法,包括 Kyber 和 ML-KEM。如果 Open Quantum Safe 达成其目标,成为 OpenSSL、BoringSSL、OpenSSH 和 Mozilla 等主要项目的首选抗量子加密实现,那么它将通过广泛的测试和使用获得显著的性能和稳健性提升。

    与本地实现相比,ML-KEM 的 Java 实现提供了直接集成到 JDK 中的关键优势。这使得它能够立即在所有已移植 JDK 的平台上可用。

测试

  • 单元测试将确认实现是否符合 KeyGeneratorKeyFactoryKEM API 的规范,包括无效输入参数、边界值和不支持的操作等边缘情况。

  • 已知答案测试(KATs)将涵盖有效的加密操作(正向案例)和无效操作或已知漏洞(负向案例),确保全面验证。这些将包括但不限于:

  • 与其他供应商的实现进行互操作性测试,包括但不限于 liboqs,将确认我们的 ML-KEM 实现能够与其他实现良好配合。