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
:
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。一个简单的单部分加密可以编写如下:
// 获取 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()
方法。以下是单部分加密的示例:
// 获取 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 的一部分提供。