跳到主要内容

JEP 329:ChaCha20 和 Poly1305 加密算法

概括

按照RFC 7539中的规定实施 ChaCha20 和 ChaCha20-Poly1305 密码。 ChaCha20 是一种相对较新的流密码,可以取代旧的、不安全的 RC4 流密码。

目标

  • 提供 ChaCha20 和 ChaCha20-Poly1305Cipher实现。这些算法将在SunJCE 提供程序中实现。
  • 提供一个KeyGenerator实现来创建适合 ChaCha20 和 ChaCha20-Poly1305 算法的密钥。
  • 提供AlgorithmParameters与 ChaCha20-Poly1305 算法一起使用的实现。

非目标

TLS 密码套件支持不会成为此 JEP 的一部分。对这些密码的 TLS 支持将是后续增强功能的一部分。

动机

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

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

描述

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

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

ChaCha20ParameterSpec(byte[] nonce, int counter);     // Constructor
public byte[] getNonce(); // Obtains a copy of the nonce value
public int getCounter(); // Obtains the initial counter value

nonce 值的长度必须为 96 位(12 字节)。任何其他长度都会导致抛出异常。整数计数器值可以是任何整数值,甚至负数,以便允许全范围的无符号 32 位值。

不带 a 的该算法的初始化将导致密码在内部生成自己的 12 字节随机数并将计数器值设置为 1。可以通过调用该方法ChaCha20ParameterSpec来获取计数器字节。Cipher.getIV()

javax.crypto.spec.IvParameterSpecChaCha20-Poly1305 的初始化可以通过提供包含 12 字节随机数的当前类的实例来完成。使用IvParameterSpecover 的决定ChaCha20ParameterSpec允许 ChaCha20-Poly1305 向后移植到早期版本,而无需进行任何 API 更改。由于IvParameterSpec没有对其保存的字节设置长度要求,因此密码对象本身将在初始化期间强制执行 12 字节长度要求。

与 ChaCha20 一样,ChaCha20-Poly1305 可以在没有 的情况下进行初始化IvParameterSpec,在这种情况下,随机数将随机生成,并且可以使用 来获得Cipher.getIV()

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

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

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec mamboSpec
= new ChaCha20ParameterSpec(nonceBytes, 7); // Use a starting counter value of "7"
// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = mambo.doFinal(pText);

对于使用 Poly1305 验证器在 AEAD 模式下运行的 ChaCha20,只需要随机数,因为 RFC 7539 定义了从 1 开始的数据初始计数器值。为了允许此 Cipher 实现可向后移植并促进其在我们的 JSSE 提供程序中使用,javax.crypto.spec.IvParameterSpec将用于提供随机数。

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

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305");
AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes);

// Encrypt our input
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()这与 AES-GCM 的加密要求类似,但在这两种密码的情况下,初始化要求必须在加密和解密操作之后发生。后续调用Cipher.update(),Cipher.updateAAD()Cipher.doFinal()在上一次调用之后并且中间doFinal()没有调用将导致抛出异常。init()``IllegalStateException

测试

测试将涵盖以下领域:

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

依赖关系

唯一重要的依赖是恒定时间数学 API。这些将作为JEP 324的一部分提供。