跳到主要内容

JEP 478:密钥派生函数 API(预览)

概括

引入密钥派生函数 (KDF) API,这是一种用于从密钥和其他数据派生附加密钥的加密算法。这是一个预览 API

目标

非目标

  • 我们的目标不是通过新的 KDF API 提供基于密码的密钥派生函数 PBKDF1 和PBKDF2的现有实现。这些实现仍可通过现有SecretKeyFactoryAPI 获得。

  • 我们的目标不是提供HKDF 的PKCS#11实现。我们打算将其作为一项单独的增强功能来提供。

  • 我们的目标不是重构 JDK 的 TLS 实现中使用的密钥派生函数以使用新的 KDF API。但是,我们可能会在后续工作中这样做。

动机

密钥派生函数 (KDF)利用加密输入(例如初始密钥材料、盐值和伪随机函数)来创建新的加密性强的密钥材料。KDF 通常用于创建可从中获取多个密钥的加密数据。KDF 允许双方以安全且可复制的方式创建密钥,双方共享输入信息。

派生密钥与散列密码类似。KDF 使用密钥散列以及来自其他输入的额外熵来提取新密钥材料或将值安全地扩展为更大的密钥材料流。

随着量子计算的出现,经典加密算法将越来越容易受到实际攻击。因此,Java 平台支持能够抵御这些攻击的后量子密码术 (PQC)至关重要。我们的目标是通过支持混合公钥加密 (HPKE) 来实现这一目标,从而实现向量子安全加密算法的平稳过渡。集成在 JDK 21 中的 KEM API ( JEP 452 ) 是 HPKE 的一个构建块,也是我们迈向 HPKE 和后量子准备的第一步;这里提出的 KDF API 是 HPKE 的另一个构建块,将是第二步。

添加 KDF API 所带来的可扩展性将带来额外的好处。加密硬件设备的PKCS#11 标准多年来一直描述 KDF 支持。通过javax.cryptoAPI提供这项技术将使与此类设备交互的应用程序和库受益。此外,第三方加密提供商的开发人员可以提供自定义 KDF 实现,特别是那些经过测试验证并随后由美国国家标准与技术研究院认证的实现。

最后,最重要的是 Java 平台为比 PBKDF1 和 PBKDF2 更复杂的密码哈希 KDF(例如 Argon2)提供更好的 API 支持。Java 平台中现有的加密 API 目前都无法以自然的方式表示 KDF(见下文。第三方安全提供商的实施者已经表达了对标准 KDF API 的需求。

描述

密钥派生函数有两个基本操作:

  • 实例化和初始化,创建 KDF 并使用适当的参数对其进行初始化,以及

  • 派生,它接受密钥材料和其他可选输入以及描述输出的参数,然后生成派生密钥或数据。

我们定义一个新类javax.crypto.KDF,来表示密钥派生函数。

这是一个预览 API,默认情况下禁用

要在 JDK 24 中使用这个新 API,您必须启用预览功能:

  • 使用 编译程序javac --release 24 --enable-preview Main.java并使用 运行它java --enable-preview Main;或者,

  • 使用源代码启动器时,使用 运行程序java --enable-preview Main.java;或者,

  • 使用时jshell,以 启动jshell --enable-preview

实例化和初始化

该类KDF提供一套常用的getInstance方法和常用的参数组合,包括可选KDFParameters和加密提供程序名称。这些getInstance方法既实例化KDF又初始化其算法。

HKDF 算法是我们目前打算纳入的唯一 KDF,它不需要KDFParameters对象。但是,其他算法(例如SHAKE)可能需要对象。

推导

该类KDF定义了两种派生密钥的方法:

我们使用空接口AlgorithmParameterSpec是因为不同的 KDF 算法采用不同的参数。KDF实现应该定义一个或多AlgorithmParameterSpec个子类来描述其参数。

对于包含的 HKDF 实现,我们提供了三个这样的子类,每个子类代表不同操作模式的输入:

包含HKDFParameterSpec接口定义了用于创建这三个类的实例的静态工厂方法。它还定义了一个构建器类,HKDFParameterSpec.Builder用于组装用于提取操作的密钥材料。

例子

// Create a KDF object for the specified algorithm
KDF hkdf = KDF.getInstance("HKDF-SHA256");

// Create an ExtractExpand parameter specification
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt).thenExpand(info, 32);

// Derive a 32-byte AES key
SecretKey key = hkdf.deriveKey("AES", params);

// Additional deriveKey calls can be made with the same KDF object

实现 KDF 提供程序

实现KDF必须扩展抽象类javax.crypto.KDFSpi

某些 KDF 算法在一次派生操作中派生出多个加密密钥。如果您要实现这样的算法,我们建议您提供一个SecretKey子类,其中包含可访问每个密钥的方法,或者在单个字节数组中返回所有密钥并记录如何分离它们。

未来工作

  • 重构 TLS 1.3 — 我们打算增强 JDK 的 TLS 1.3 实现,以使用 KDF API 通过 HKDF 派生密钥。届时,我们将添加新的功能测试,以证明通过新 API 检索 KDF 实现的有效性。

    最初,我们不打算对 1.3 之前的 TLS 版本执行此操作。

  • 实施 Argon2 — 我们最终打算实施 Argon2 密码哈希 KDF。

替代方案

  • 使用现有 API — 我们考虑使用现有KeyGeneratorSecretKeyFactoryAPI 来表示 KDF。早期的密钥派生算法(例如 TLS-PRF、PBKDF1 和 PBKDF2)已适合这些 API,但一般来说,这些 API 不适用于 KDF。

    • KeyGenerator围绕通过对象引入熵而设计SecureRandom,以便从一组输入中创建非确定性密钥。相比之下,KDF 支持由两个独立方独立派生同一密钥材料。

    • SecretKeyFactory旨在创建单个密钥。尽管在某些情况下可以以这种方式使用 KDF,但 KDF 还需要支持以确定性方式从密钥流连续派生。

  • KDF通过新API提供 PBKDF2 — PBKDF2 正在迅速被更强大的算法(例如 Argon2)取代。已经使用 PBKDF2 的开发人员可能会继续通过 API 使用它SecretKeyFactory,而不是重构现有代码以使用该KDFAPI。因此,目前没有必要通过新 API 提供 PBKDF2,但如果出现迫切需求,我们可以稍后这样做。

测试

如果可用,我们将添加 RFC 5869 已知答案 (KAT) 测试,以及异常处理测试和 SSL/TLS 回归测试。