JEP 478: 密钥导出函数 API(预览)
概要
介绍一种用于密钥导出函数(KDFs)的 API,这是一种从密钥和其他数据中导出附加密钥的加密算法。这是一个预览 API。
目标
非目标
动机
密钥导出函数 (KDFs) 利用诸如初始密钥材料、盐值和伪随机函数等加密输入来创建新的强加密密钥材料。KDF 通常用于从可以获取多个密钥的加密数据中创建这些密钥。KDF 允许以一种安全且可由共享输入知识的两方重现的方式来创建密钥。
派生密钥类似于哈希密码。KDF 使用加盐哈希以及来自其其他输入的额外熵来提取新的密钥材料,或者将值安全地扩展为更大的密钥材料流。
随着量子计算的出现,经典的加密算法将越来越容易受到实际攻击。因此,对于 Java 平台来说,支持后量子密码学 (PQC) 是至关重要的,因为这种密码学可以抵御这些攻击。我们最终的目标是通过支持混合公钥加密(HPKE)来实现这一目标,这使得向抗量子加密算法的平稳过渡成为可能。KEM API(JEP 452)已经在 JDK 21 中集成,它是 HPKE 的一个构建模块,也是我们迈向 HPKE 和后量子准备的第一步;这里提出的 KDF API 是 HPKE 的另一个构建模块,它将是第二步。
通过添加 KDF API 所提供的可扩展性将带来额外的好处。用于加密硬件设备的 PKCS#11 标准 多年来已经描述了对 KDF 的支持。通过 javax.crypto
API 提供这项技术将使与这些设备交互的应用程序和库受益。此外,第三方加密提供商的开发人员可能会提供自定义的 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
运行它;或者, -
当使用 source code launcher 时,使用
java --enable-preview Main.java
运行程序;或者, -
当使用
jshell
时,使用jshell --enable-preview
启动它。
实例化和初始化
KDF
类提供了常用的 getInstance
方法,这些方法具有通常的参数组合,包括可选的 KDFParameters
和加密提供程序名称。getInstance
方法既实例化一个 KDF
,又初始化其算法。
HKDF 算法是目前我们唯一打算包含的 KDF,它不需要 KDFParameters
对象。然而,其他算法,如 SHAKE,可能需要它。
推导
KDF
类定义了两种派生密钥的方法:
-
deriveKey(String alg, AlgorithmParameterSpec spec
) 使用指定的算法和参数构造一个SecretKey
对象。 -
deriveData(AlgorithmParameterSpec spec)
返回一个字节数组,可用于熵或需要以该形式提供密钥材料时。
我们使用空接口 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 — 我们考虑过使用现有的
KeyGenerator
和SecretKeyFactory
API 来表示 KDF。早期的密钥派生算法,如 TLS-PRF、PBKDF1 和 PBKDF2 已经被适配到这些 API 中,但总体来说,这些 API 对于 KDF 并不适用。-
KeyGenerator
是围绕通过SecureRandom
对象引入熵来创建一组输入的非确定性密钥而设计的。相比之下,KDF 支持两个独立方独立地派生相同的密钥材料。 -
SecretKeyFactory
用于创建单个密钥。虽然在某些场景中可以这样使用 KDF,但 KDF 也要求支持以确定的方式从密钥流中连续派生。
-
-
通过新的
KDF
API 提供 PBKDF2 — PBKDF2 正迅速被更强的算法(如 Argon2)所取代。已经使用 PBKDF2 的开发人员可能会继续通过SecretKeyFactory
API 使用它,而不是重构现有代码以使用KDF
API。因此,目前通过新 API 提供 PBKDF2 是不必要的,但如果出现强烈需求,我们可以在以后这样做。
测试
我们将添加 RFC 5869 已知答案(KAT)测试(如果可用),以及异常处理测试和 SSL/TLS 回归测试。