JEP 329:ChaCha20 和 Poly1305 加密算法
概括
按照RFC 7539中的规定实施 ChaCha20 和 ChaCha20-Poly1305 密码。 ChaCha20 是一种相对较新的流密码,可以取代旧的、不安全的 RC4 流 密码。
目标
- 提供 ChaCha20 和 ChaCha20-Poly1305
Cipher
实现。这些算法将在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.IvParameterSpec
ChaCha20-Poly1305 的初始化可以通过提供包含 12 字节随机数的当前类的实例来完成。使用IvParameterSpec
over 的决定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的一部分提供。