Java AES CTR IV 和计数器
Java AES CTR IV and counter
我已经尝试通过自己在此处查看并在其他地方搜索了很长时间来找到答案,但我仍然有一些疑问。
假设这个 Java 代码:
try
{
int cipherMode = Cipher.ENCRYPT_MODE;
SecretKeySpec secretKey = ...; // generated previously using KeyGenerator
byte[] nonceAndCounter = new byte[16];
byte[] nonceBytes = ...; // generated previously using SecureRandom's nextBytes(8);
// use first 8 bytes as nonce
Arrays.fill(nonceAndCounter, (byte) 0);
System.arraycopy(nonceBytes, 0, nonceAndCounter, 0, 8);
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(cipherMode, secretKey, ivSpec);
File inFile = new File(...);
File outFile = new File(...);
long bytesRead = 0;
try (FileInputStream is = new FileInputStream(inFile);
FileOutputStream os = new FileOutputStream(outFile))
{
byte[] inBuf = new byte[512 * 1024];
byte[] outBuf = new byte[512 * 1024];
int readLen = 0;
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.putLong(bytesRead);
while ((readLen = is.read(inBuf)) != -1)
{
bytesRead += readLen;
cipher.update(inBuf, 0, readLen, outBuf, 0);
os.write(outBuf);
}
cipher.doFinal(outBuf, 0);
os.write(outBuf);
is.close();
os.close();
}
catch (Exception e) {
System.out.printf("Exception for file: %s\n", e);
}
}
catch (Exception e) {
System.out.printf("Exception: %s\n", e);
}
我的问题是:
关于 CTR 模式的计数器更新,上面的代码是否被认为是正确的?具体来说,我不会自己更新计数器。我应该改用下面的 while 循环吗?我试过这个是因为我查看了循环中的 cipher.getIV() returns ,但它没有改变并且 getIV() 的描述没有详细说明:
while ((readLen = is.read(inBuf)) != -1)
{
// use offset for last 8 bytes as counter
byteBuffer.putLong(bytesRead);
System.arraycopy(byteBuffer.array(), 0, nonceAndCounter, 8, 8);
bytesRead += readLen;
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
cipher.init(cipherMode, secretKey, ivSpec);
cipher.update(inBuf, 0, readLen, outBuf, 0);
os.write(outBuf);
}
我有更多关于修改后的 while 循环方法的问题。这样调用cipher.init()可以吗?我这样做是因为我还没有找到只更新 IV 的方法(真的是计数器)。
这么大的块大小可以还是应该变小?那么应该多大呢?
而不是像您在此处那样生成 iv/nonce:
// use first 8 bytes as nonce
Arrays.fill(nonceAndCounter, (byte) 0);
System.arraycopy(nonceBytes, 0, nonceAndCounter, 0, 8);
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(cipherMode, secretKey, ivSpec);
您应该考虑执行以下操作:
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// By doing this here w/o an IvParameterSpec, you let the
// cipher initialization create it. Less chance (see what I did there)
// to influence values that should be completely random.
cipher.init(cipherMode, secretKey);
AlgorithmParameters ivSpec = cipher.getParameters();
byte[] nonceAndCounter= ivSpec.getEncoded()
在接收方,当您需要根据返回给 nonceAndCounter 的值构建 IvParameterSpec,然后构建您的解密模式时。
- Is the above code considered OK regarding counter updates for CTR mode?
是。
但您可能想要稍微调整一下随机数和计数器的大小。如果你的随机数只有 64 位长,由于生日悖论,你可能会在 232 次加密后 运行 发生随机数冲突(如果你接近那个概率会增加观点)。如果你对所有这些加密使用相同的密钥(我的意思是 messages/files 而不是块)并且发生了冲突,这被认为是 CTR 模式的灾难性中断,因为它是两次或多次软垫。
您应该考虑使用 96 位随机数和 32 位计数器。缺点是您最多只能安全地加密 232 个块,大约每个 message/file.
68 GB
- I have more questions related to the modified while loop approach. Is it OK to call cipher.init() in such a way?
没有
你真的不应该自己更新计数器。请注意,字节和块密码块之间存在不匹配:您建议的计数器更新使用已经处理过的字节作为新计数器值,它比按块计数的自然 CTR 模式计数器前进得更快。您正在耗尽计数器,因此碰撞的可能性越来越大。例如,如果在用数字表示时 nonce 相差 1,则如果 nonce 部分较短而计数器部分较长,则可能会发生重叠。如果随机数是 96 位长,那么您只能安全地加密大小为 68/16 GB = 4.5 GB 的 messages/files。
此外,由于您有 byte/block 不匹配,这不再是 CTR 模式,您将很难将此代码移植到其他语言。
- Is such a large block size OK or should it be made smaller? In that case how big should it be?
不太清楚你的意思,AES 的块大小固定为 128 位或 16 字节。
如果您指的是 input/output 缓冲区,那么您应该在您选择的平台上对其进行基准测试以确保。看起来确实不错。
我已经尝试通过自己在此处查看并在其他地方搜索了很长时间来找到答案,但我仍然有一些疑问。
假设这个 Java 代码:
try
{
int cipherMode = Cipher.ENCRYPT_MODE;
SecretKeySpec secretKey = ...; // generated previously using KeyGenerator
byte[] nonceAndCounter = new byte[16];
byte[] nonceBytes = ...; // generated previously using SecureRandom's nextBytes(8);
// use first 8 bytes as nonce
Arrays.fill(nonceAndCounter, (byte) 0);
System.arraycopy(nonceBytes, 0, nonceAndCounter, 0, 8);
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(cipherMode, secretKey, ivSpec);
File inFile = new File(...);
File outFile = new File(...);
long bytesRead = 0;
try (FileInputStream is = new FileInputStream(inFile);
FileOutputStream os = new FileOutputStream(outFile))
{
byte[] inBuf = new byte[512 * 1024];
byte[] outBuf = new byte[512 * 1024];
int readLen = 0;
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.putLong(bytesRead);
while ((readLen = is.read(inBuf)) != -1)
{
bytesRead += readLen;
cipher.update(inBuf, 0, readLen, outBuf, 0);
os.write(outBuf);
}
cipher.doFinal(outBuf, 0);
os.write(outBuf);
is.close();
os.close();
}
catch (Exception e) {
System.out.printf("Exception for file: %s\n", e);
}
}
catch (Exception e) {
System.out.printf("Exception: %s\n", e);
}
我的问题是:
关于 CTR 模式的计数器更新,上面的代码是否被认为是正确的?具体来说,我不会自己更新计数器。我应该改用下面的 while 循环吗?我试过这个是因为我查看了循环中的 cipher.getIV() returns ,但它没有改变并且 getIV() 的描述没有详细说明:
while ((readLen = is.read(inBuf)) != -1) { // use offset for last 8 bytes as counter byteBuffer.putLong(bytesRead); System.arraycopy(byteBuffer.array(), 0, nonceAndCounter, 8, 8); bytesRead += readLen; IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter); cipher.init(cipherMode, secretKey, ivSpec); cipher.update(inBuf, 0, readLen, outBuf, 0); os.write(outBuf); }
我有更多关于修改后的 while 循环方法的问题。这样调用cipher.init()可以吗?我这样做是因为我还没有找到只更新 IV 的方法(真的是计数器)。
这么大的块大小可以还是应该变小?那么应该多大呢?
而不是像您在此处那样生成 iv/nonce:
// use first 8 bytes as nonce
Arrays.fill(nonceAndCounter, (byte) 0);
System.arraycopy(nonceBytes, 0, nonceAndCounter, 0, 8);
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(cipherMode, secretKey, ivSpec);
您应该考虑执行以下操作:
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// By doing this here w/o an IvParameterSpec, you let the
// cipher initialization create it. Less chance (see what I did there)
// to influence values that should be completely random.
cipher.init(cipherMode, secretKey);
AlgorithmParameters ivSpec = cipher.getParameters();
byte[] nonceAndCounter= ivSpec.getEncoded()
在接收方,当您需要根据返回给 nonceAndCounter 的值构建 IvParameterSpec,然后构建您的解密模式时。
- Is the above code considered OK regarding counter updates for CTR mode?
是。
但您可能想要稍微调整一下随机数和计数器的大小。如果你的随机数只有 64 位长,由于生日悖论,你可能会在 232 次加密后 运行 发生随机数冲突(如果你接近那个概率会增加观点)。如果你对所有这些加密使用相同的密钥(我的意思是 messages/files 而不是块)并且发生了冲突,这被认为是 CTR 模式的灾难性中断,因为它是两次或多次软垫。
您应该考虑使用 96 位随机数和 32 位计数器。缺点是您最多只能安全地加密 232 个块,大约每个 message/file.
68 GB
- I have more questions related to the modified while loop approach. Is it OK to call cipher.init() in such a way?
没有
你真的不应该自己更新计数器。请注意,字节和块密码块之间存在不匹配:您建议的计数器更新使用已经处理过的字节作为新计数器值,它比按块计数的自然 CTR 模式计数器前进得更快。您正在耗尽计数器,因此碰撞的可能性越来越大。例如,如果在用数字表示时 nonce 相差 1,则如果 nonce 部分较短而计数器部分较长,则可能会发生重叠。如果随机数是 96 位长,那么您只能安全地加密大小为 68/16 GB = 4.5 GB 的 messages/files。
此外,由于您有 byte/block 不匹配,这不再是 CTR 模式,您将很难将此代码移植到其他语言。
- Is such a large block size OK or should it be made smaller? In that case how big should it be?
不太清楚你的意思,AES 的块大小固定为 128 位或 16 字节。
如果您指的是 input/output 缓冲区,那么您应该在您选择的平台上对其进行基准测试以确保。看起来确实不错。