Java crypto,重置 IV 可以提高性能吗?
Java crypto, reset the IV possible for performance improvement?
我创建了一个加密密码如下(在 Scala 中,使用 bouncy-castle)
def encryptCipher(secret:SecretKeySpec, iv:IvParameterSpec):Cipher = {
val e = Cipher.getInstance("AES/GCM/NoPadding")
e.init(Cipher.ENCRYPT_MODE, secret, iv)
}
您看到生成密钥规范的缓慢操作已经得到处理。然而,为每条消息调用 init 本身太慢了。
我目前正在处理 50K 条消息,调用 init 方法增加了将近 4 秒。
有没有一种方法可以用不那么耗时的新 IV 重新初始化?
标准库中没有这样做的标准方法,
但如果您使用的是 AES,则有一个很好的解决方法:
IV 的目的是消除相同明文加密成相同密文的可能性。
您可以 "update"(如 Cipher.update(byte[])
)在加密之前使用随机块大小的字节数组(在解密时使用相同的块)。这与使用与 IV 相同的随机块几乎完全相同。
要看到,运行这个片段(使用上述方法生成完全相同的密文 - 但这只是为了与其他平台兼容,无需计算特定的 IV 即可确保安全。
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[16];
secureRandom.nextBytes(keyBytes);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
byte[] plain = new byte[256];
secureRandom.nextBytes(plain);
// first init using random IV (save it for later)
cipher.init(Cipher.ENCRYPT_MODE, key, secureRandom);
byte[] realIv = cipher.getIV();
byte[] expected = cipher.doFinal(plain);
// now init using dummy IV and encrypt with real IV prefix
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, key, nullIv);
// calculate equivalent iv
Cipher equivalentIvAsFirstBlock = Cipher.getInstance("AES/CBC/NoPadding");
equivalentIvAsFirstBlock.init(Cipher.DECRYPT_MODE, key, nullIv);
byte[] equivalentIv = equivalentIvAsFirstBlock.doFinal(realIv);
cipher.update(equivalentIv);
byte[] result = cipher.doFinal(plain);
System.out.println(Arrays.equals(expected, result));
解密部分比较简单,因为块解密的结果与之前的密文进行了异或(见Block cipher mode of operation),你只需要将真实的IV附加到密文中,然后抛出它之后:
// Encrypt as before
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.DECRYPT_MODE, key, nullIv);
cipher.update(realIv);
byte[] result = cipher.doFinal(encrypted);
// result.length == plain.length + 16
// just throw away the first block
我创建了一个加密密码如下(在 Scala 中,使用 bouncy-castle)
def encryptCipher(secret:SecretKeySpec, iv:IvParameterSpec):Cipher = {
val e = Cipher.getInstance("AES/GCM/NoPadding")
e.init(Cipher.ENCRYPT_MODE, secret, iv)
}
您看到生成密钥规范的缓慢操作已经得到处理。然而,为每条消息调用 init 本身太慢了。
我目前正在处理 50K 条消息,调用 init 方法增加了将近 4 秒。
有没有一种方法可以用不那么耗时的新 IV 重新初始化?
标准库中没有这样做的标准方法, 但如果您使用的是 AES,则有一个很好的解决方法:
IV 的目的是消除相同明文加密成相同密文的可能性。
您可以 "update"(如 Cipher.update(byte[])
)在加密之前使用随机块大小的字节数组(在解密时使用相同的块)。这与使用与 IV 相同的随机块几乎完全相同。
要看到,运行这个片段(使用上述方法生成完全相同的密文 - 但这只是为了与其他平台兼容,无需计算特定的 IV 即可确保安全。
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[16];
secureRandom.nextBytes(keyBytes);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
byte[] plain = new byte[256];
secureRandom.nextBytes(plain);
// first init using random IV (save it for later)
cipher.init(Cipher.ENCRYPT_MODE, key, secureRandom);
byte[] realIv = cipher.getIV();
byte[] expected = cipher.doFinal(plain);
// now init using dummy IV and encrypt with real IV prefix
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, key, nullIv);
// calculate equivalent iv
Cipher equivalentIvAsFirstBlock = Cipher.getInstance("AES/CBC/NoPadding");
equivalentIvAsFirstBlock.init(Cipher.DECRYPT_MODE, key, nullIv);
byte[] equivalentIv = equivalentIvAsFirstBlock.doFinal(realIv);
cipher.update(equivalentIv);
byte[] result = cipher.doFinal(plain);
System.out.println(Arrays.equals(expected, result));
解密部分比较简单,因为块解密的结果与之前的密文进行了异或(见Block cipher mode of operation),你只需要将真实的IV附加到密文中,然后抛出它之后:
// Encrypt as before
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.DECRYPT_MODE, key, nullIv);
cipher.update(realIv);
byte[] result = cipher.doFinal(encrypted);
// result.length == plain.length + 16
// just throw away the first block