针对AES加密的随机IV情况下每条消息的Cipher.getInstance()和Cipher.getInit()

Ama*_*man 6 java encryption aes

在多线程Java应用程序中,我们使用AES-256对磁盘进行文件加密和解密.请注意,多个线程可以同时调用不同文件的加密和解密方法.

加密:

Cipher encrypter = Cipher.getInstance(algorithm, new BouncyCastleProvider());
IvParameterSpec ivSpec = getIvParamSpec(encrypter.getBlockSize());
encrypter.init(Cipher.ENCRYPT_MODE, key, ivSpec);
//..encrypt the data
Run Code Online (Sandbox Code Playgroud)

解密:

Cipher decrypter = Cipher.getInstance(algorithm, new BouncyCastleProvider());
IvParameterSpec ivSpec = readIvParamSpec(decrypter.getBlockSize(), is);
decrypter.init(Cipher.DECRYPT_MODE, key, ivSpec);
//.. decrypt the data
Run Code Online (Sandbox Code Playgroud)

根据我们的理解,最好使用随机IV进行加密而不是静态/固定IV.为此,我们使用SecureRandom API生成IV.随机IV在开始时保存在加密文件中.

SecureRandom random = new SecureRandom();
byte[] iv = new byte[ivSizeBytes];
random.nextBytes(iv);
new IvParameterSpec(iv);
Run Code Online (Sandbox Code Playgroud)

我的问题是,因为我使用随机IV进行每次加密,我是否需要为所有调用调用Cipher.getInstance()和Cipher.Init()?为了提高性能,这些只能在类初始化期间调用一次,然后重用单个密码实例来加密和解密数据吗?

提前致谢!

Art*_* B. 7

是的,您需要Cipher为每个线程使用不同的实例,因为它们是有状态的.如果不这样做,那么线程可以破坏其他线程的密文.

假设我们有两个线程t1,t2它们想要加密两个明文p1_1 | p1_2p2_1 | p2_1(在块边界上分开).我们以CBC为例:

time........................................................................
root    1. init with IV                           
t1          2. E(p1_1 XOR IV) = c1_1          4. E(p1_2 XOR c2_1) = c1_2
t2              3. E(p2_1 XOR c1_1) = c2_1        5. E(p2_2 XOR c1_2) = c2_2
Run Code Online (Sandbox Code Playgroud)

c1_1没关系,但是c2_1不行,因为状态from t1用于启动加密.好像加密是用c1_1IV 初始化的.

此示例仅适用于CBC模式,但其他操作模式类似.如果我们假设加密只是按块进行,那么你可以以线程安全的方式使用ECB模式,但这只是一种幻觉,因为你不能确定实现只是按块处理内部状态而不是按字节.


Maa*_*wes 6

我的问题是,由于我对每种加密使用随机IV,是否需要为所有调用调用Cipher.getInstance()和Cipher.Init()?

只要不在线程之间共享它们,就可以重用Cipher实例。如Artjom所述,密码实例是有状态的。它们既存储IV,又存储最后的密文(用作CB​​C模式加密的下一个向量),还可能存储一些缓冲的明文。将该状态与来自不同线程的输入混合会导致混乱。

由于每个文件加密都需要一个新的随机数,因此您确实需要init在调用doFinal方法后再次调用。Init并不是那么重量级;可能需要一点性能的一件事是AES的子密钥派生,但通常这不是大问题。

注意,尽管执行加密和解密可能是比较繁重的操作,但是实例本身包含很少的状态,getInstance()并且init是相对轻量级的操作。因此,创建多个Cipher实例(可能使用相同的键)对于多个线程来说是很好的。


重新创建BouncyCastleProvider多次一个非常糟糕的主意,即使它可能在下面使用某种单例。但基本上,您不需要仅Java的 Bouncy Castle实现。Oracle可能会使用AES-NI内部函数,而这些函数将直接使用兼容处理器上的AES-NI指令集。它将在Bouncy Castle周围绕圈-预计加速约7到13倍(!)。

  • 如果这是因为 BC 提供程序而不是密码的重复实例化,我不会感到惊讶。您可以尝试从等式中删除提供程序吗(完全删除参数可以做到(给您 Oracle 一个),或者只是共享提供程序,或使用“Security.addProvideder(new BouncyCastleProvider())”然后使用名称`“BC”`(等)。 (2认同)