跳到主要内容

JEP 324:与 Curve25519 和 Curve448 的关键协议

概括

使用 Curve25519 和 Curve448 实现密钥协商,如RFC 7748中所述。

目标

RFC 7748定义了一种比现有椭圆曲线Diffie-Hellman(ECDH)方案更高效、更安全的密钥协商方案。该 JEP 的主要目标是 API 和该标准的实现。其他实施目标是:

  1. 开发独立于平台的全 Java 实现,在相同的安全强度下,其性能比现有 ECC(本机 C)代码更好。
  2. 假设平台在恒定时间内执行 64 位整数加法/乘法,请确保计时与秘密无关。此外,实施不会因秘密而分支。这些属性对于防止旁路攻击非常有价值。

非目标

RFC 7748 将仅在 SunEC 提供商中实施。在其他提供商中实施该标准并不是本 JEP 的目标。

SunEC 中的实现不支持任意域参数。 JCA API 应该允许通过扩展指定任意域参数。此类扩展超出了本 JEP 的范围。

成功指标

  1. RFC 7748 中的所有测试向量均通过。
  2. 吞吐量(按照现有密钥协议基准中每秒派生的密钥来衡量)将优于所有平台上现有的 ECC 实现(具有类似的安全强度)。
  3. 统计测试(需要开发)将表明密钥协商操作的时间不会随私钥的变化而变化。

动机

使用 Curve25519 和 Curve448 的加密技术由于其安全性和性能特性而受到欢迎。许多其他加密库(例如 OpenSSL、BoringSSL 和 BouncyCastle)已支持使用这些曲线进行密钥交换。此密钥交换机制是 TLS 1.3 的可选组件,并通过常用扩展在早期 TLS 版本中启用。

描述

X25519 和 X448 功能将按照 RFC 7748 中的描述来实现,这些功能将用于在现有 SunEC 提供商中实现新的KeyAgreementKeyFactory、 和服务。KeyPairGenerator该实现将使用 RFC 7748 中描述的恒定时间蒙哥马利梯形方法,以防止侧信道攻击。该实现将通过将结果与 0 进行比较来确保贡献行为,如 RFC 中所述。

大数算术将使用新的模块化算术库来执行。与此库相比,该库有两个显着的优点BigInteger

  1. 大多数操作将在恒定时间内执行。如果这些操作数在任何相关算法中都不是秘密,则某些操作可能会随其操作数的大小而变化。例如,求幂的时序将随指数的大小而变化,但指数值在 RFC 7748 中并不是秘密。该库的 API 文档将描述每个操作的时序行为。
  2. 通过避免进位运算并利用 EC 运算中使用的特定有限域的属性,可以提高性能。

这个新库将位于内部 JDK 包中,并且仅由新的加密算法使用。我们预计在不久的将来将其用于 EdDSA(使用 Curve25519 和 Curve448 的签名)和 Poly1305(消息身份验证)实现中。该库使用受EdDSA 论文中的启发的简化基数表示法。

由于该算法避免了进位运算,因此如果存在错误或使用不正确,则可能会溢出并产生错误的结果。例如,加法运算不进位,因此在每个乘法运算之前可以执行有限数量(通常是一个)的加法运算。该库将包含针对误用的防御措施(例如,如果发生太多添加,则抛出异常),并且必须仔细测试以确保它不会溢出。

应用程序编程接口

RFC 7748 的 JCA API 将使用名称“XDH”来标识与此机制相关的所有服务(KeyAgreementKeyPairGeneratorKeyFactory等)。算法名称“X25519”和“X448”也将分别定义为使用 Curve25519 和 Curve448 的 XDH。这允许方便的速记(例如,KeyPairGenerator.getInstance("X448")),也使得更容易找到支持所需曲线的提供者。像“X25519”和“X448”这样的名称不应该简单地是“XDH”的别名——为这些名称返回的服务应该使用正确的曲线进行初始化,并且它可能会拒绝任何使用不同曲线的键。

AlgorithmParameterSpec:一个名为的新类NamedParameterSpec将用于指定使用哪条曲线(X25519 或 X448)。此类使用单个标准名称来指定一组参数,并且旨在由使用命名参数的其他算法重用。例如,它可用于(有限域)Diffie-Hellman 中的命名组。NamedParameterSpec将被插入到上面的类层次结构中ECGenParameterSpec,因此这ECGenParameterSpec也将是一个NamedParameterSpec.

KeySpec:新类XECPublicKeySpec可用于指定公钥。这个类有一个BigInteger成员保存u点的坐标。新类XECPrivateKeySpec可用于指定私钥。此类有一个字节数组成员,用于保存kRFC 7748 中描述的 X25519 和 X448 函数的(编码)输入。这两个KeySpec类都有一个AlgorithmParameterSpec指定曲线和其他算法参数的成员。

现有的X509EncodedKeySpec类也可用于公钥。现有的PKCS8EncodedKeySpec类也可用于私钥。

Key接口:将添加新接口XECPublicKeyXECPrivateKey提供对关键对象中包含的信息的访问。这些接口中关键数据的表示将与XECPublicKeySpec和中的表示相同XECPrivateKeySpec。两个接口都将从新接口扩展,新接口将提供对定义曲线和其他算法参数XECKey的访问。AlgorithmParameterSpec

API 使用示例:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
// alternatively: kpg = KeyPairGenerator.getInstance("X25519")
KeyPair kp = kpg.generateKeyPair();

KeyFactory kf = KeyFactory.getInstance("XDH");
BigInteger u = ...
XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
PublicKey pubKey = kf.generatePublic(pubSpec);

KeyAgreement ka = KeyAgreement.getInstance("XDH");
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();

备择方案

考虑了与实施相关的一些替代方案:

  1. 可以使用 执行字段算术BigInteger。初始原型表明,它BigInteger比自定义模块化算术库慢大约 10 倍。例如,BigInteger在初始测试中,X25519 大约需要 2.5 毫秒,而在同一平台上运行的自定义库则需要 0.25 毫秒。此外,BigInteger没有恒定时间乘法,这将消除这些函数的一些旁道阻力。
  2. 本机实现(例如现有的 ECC 代码)可以提供更好的性能。初始原型显示 Java 实现对于典型用途而言足够快。例如,X25519(Java 中)大约需要 0.25 毫秒,而 secp256r1 组操作(C 中)在同一平台上大约需要 1 毫秒。因此,初步结果表明 Java 实现应该足够快。
  3. 可以使用现有的 ECC 代码来实现此密钥协议,但此方法无法提供 RFC 7748 的所有安全/性能优势。

测试

测试将包括 RFC 7748 中的测试向量。这些向量包括 X25519 和 X448 函数的一百万次迭代,大约需要 15 分钟才能完成。如果我们想定期运行它们,我们可以将它们分成每批 10-10 万个的批次,从而实现并行化。

测试算术库会稍微更具挑战性,因为导致溢出的条件可能不太可能自然发生。用于 Curve25519 和 Curve448 的表示形式应与严格的数学证明一起开发,以证明它们不会溢出。这些证明将包含与可以纳入回归测试的每个基础操作(加法、乘法、进位、归约)相关的边界条件。

风险和假设

一个重大风险是模块化算术实现的复杂性和微妙性。通过对算法的正确性进行严格的证明并进行彻底的测试,可以减轻这种风险。