Skip to content

JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms | ChaCha20 和 Poly1305 加密算法

摘要

实现按照 RFC 7539 规定的 ChaCha20 和 ChaCha20-Poly1305 密码算法。ChaCha20 是一个相对较新的流密码,可以替代较旧且不安全的 RC4 流密码。

目标

  • 提供 ChaCha20 和 ChaCha20-Poly1305 的 Cipher 实现。这些算法将在 SunJCE 提供者中实现。
  • 提供一个 KeyGenerator 实现,用于生成适用于 ChaCha20 和 ChaCha20-Poly1305 算法的密钥。
  • 为 ChaCha20-Poly1305 算法提供一个 AlgorithmParameters 实现。

非目标

TLS 密码套件支持将不会作为本 JEP 的一部分。这些密码的 TLS 支持将是后续增强的内容。

动机

唯一被广泛采用的另一个流密码 RC4,长期以来被认为是不安全的。业界共识是,ChaCha20-Poly1305 在目前的时间点是安全的,并且在 TLS 实现以及其他加密协议中已经得到了相当广泛的采用。JDK 需要与其他加密工具包和 TLS 实现保持同步。

此外,TLS 1.3 只允许使用基于 AEAD 的密码套件。实现 ChaCha20-Poly1305 算法是在 AES 或 GCM 中发现任何弱点时,实现运行在 AEAD 模式下的不同密码套件的第一步。

描述

ChaCha20 和 ChaCha20-Poly1305 算法将在 SunJCE 提供者中实现 javax.crypto.CipherSpi API。密码的实例化方式与其他密码相同,都使用 Cipher.getInstance() 方法。对于这两种密码,都允许使用两种可接受的转换。首选的单名转换是,对于没有认证的简单流密码 ChaCha20 使用 "ChaCha20",而对于使用 Poly1305 作为认证器的 AEAD 密码 ChaCha20 使用 "ChaCha20-Poly1305"。虽然除 "None""NoPadding" 之外,不接受其他模式或填充值,但 "ChaCha20/None/NoPadding""ChaCha20-Poly1305/None/NoPadding" 也是可接受的转换字符串。使用其他模式或填充值将引发异常。

ChaCha20 密码的初始化将接受一个新的 AlgorithmParameterSpec 实现,即 javax.crypto.spec.ChaCha20ParameterSpec

java
ChaCha20ParameterSpec(byte[] nonce, int counter);     // 构造函数
public byte[] getNonce();     // 获取 nonce 值的副本
public int getCounter();     // 获取初始计数器值

nonce 值必须为 96 位长度(12 个字节)。任何其他长度都将引发异常。整数计数器值可以是任何整数值,甚至是负数,以便允许无符号 32 位值的完整范围。

如果不使用 ChaCha20ParameterSpec 初始化此算法,密码将内部生成自己的 12 字节 nonce,并将计数器值设置为 1。可以通过调用 Cipher.getIV() 方法来获取计数器字节。

ChaCha20-Poly1305 的初始化可以通过提供当前 javax.crypto.spec.IvParameterSpec 类的实例来完成,该类包含 12 字节的 nonce。决定使用 IvParameterSpec 而不是 ChaCha20ParameterSpec 允许 ChaCha20-Poly1305 在不做任何 API 更改的情况下回退到早期版本。由于 IvParameterSpec 对其持有的字节没有设置长度要求,密码对象本身将在初始化期间强制执行 12 字节长度的要求。

与 ChaCha20 一样,ChaCha20-Poly1305 可以在没有 IvParameterSpec 的情况下进行初始化,在这种情况下,nonce 将随机生成,并可通过 Cipher.getIV() 获取。

通过任何 init 方法提供的密钥对象必须具有“ChaCha20”的算法类型。将创建新的 KeyGenerator 实现来支持这一点。与现有的 AES、RC2、ARCFOUR 和 HmacSHA2 系列等算法的 KeyGenerator 实现一样,此 KeyGenerator 可能无法使用 AlgorithmParameterSpec 进行初始化。如果调用允许调整密钥长度的 init 方法形式,则必须将该参数设置为 256,否则会抛出 InvalidParameterException

ChaCha20 算法的使用遵循用于其他流密码的现有 Cipher API。一个简单的单部分加密可以编写如下:

java
// 获取 Cipher 实例并设置参数
// 假设 SecretKey "key"、12 字节的 nonce "nonceBytes"和明文"pText"来自此代码片段外部
Cipher mambo = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec mamboSpec
    = new ChaCha20ParameterSpec(nonceBytes, 7);   // 使用起始计数器值“7”
// 加密输入
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = mambo.doFinal(pText);

对于使用 Poly1305 认证器的 ChaCha20 在 AEAD 模式下运行时,仅需要 nonce,因为 RFC 7539 定义了数据开始的初始计数器值为 1。为了允许此 Cipher 实现向后兼容并促进在我们的 JSSE 提供者中的使用,将使用 javax.crypto.spec.IvParameterSpec 来提供 nonce。

在 AEAD 模式下运行时,由于添加了认证标签(对于加密)或消耗并验证标签(对于解密),输出大小可能与输入不同。在加密 / 解密之前需要分配输出缓冲区时,应使用 getOutputSize() 方法。以下是单部分加密的示例:

java
// 获取 Cipher 实例并设置参数
// 假设 SecretKey "key"、12 字节的 nonce "nonceBytes"和明文"pText"来自此代码片段外部
Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305");
AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes);

// 加密输入
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = new byte[mambo.getOutputSize(pText.length)];
mambo.doFinal(pText, 0, pText.length, encryptedResult);

对于 ChaCha20 和 ChaCha20-Poly1305 加密算法,一个重要的要求是,在调用 doFinal() 方法之后,必须进行一个新的 init() 调用,并提供一个与当前配置的 nonce 不同的 nonce。这与 AES-GCM 加密算法的要求类似,但在这两种加密算法的情况下,初始化要求必须在加密和解密操作之后进行。如果在之前的 doFinal() 调用之后且没有中间的 init() 调用,随后调用 Cipher.update()Cipher.updateAAD()Cipher.doFinal() 将会导致抛出 IllegalStateException

测试

测试将覆盖以下领域:

  • 验证 ChaCha20 和 ChaCha20-Poly1305 加密算法是否通过所有已知答案测试
  • 验证 ChaCha20 密钥生成器是否接受正确的初始化形式,并生成适当大小的密钥
  • 验证加密算法是否处理初始化限制(例如不重复使用 nonce 等)
  • 验证加密算法在完整的加密 / 解密操作之间是否执行了适当的重新初始化要求
  • 验证我们的实现是否与至少一个其他实现互操作
  • 验证 ChaCha20-Poly1305 的 AlgorithmParameters 实现是否接受正确格式的 nonce 数据。

依赖项

唯一的显著依赖项是恒时数学 API。这些将作为 JEP 324 的一部分提供。