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

时间:2017-05-15 22:06:06

标签: 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

解密:

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

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

SecureRandom random = new SecureRandom();
byte[] iv = new byte[ivSizeBytes];
random.nextBytes(iv);
new IvParameterSpec(iv);

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

提前致谢!

2 个答案:

答案 0 :(得分:5)

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

假设我们有两个线程t1t2,它们要加密两个明文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

c1_1没问题,但c2_1不正常,因为来自t1的状态用于启动加密。好像加密是用c1_1作为IV初始化的。

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

答案 1 :(得分:4)

  

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

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

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

请注意,虽然执行加密和解密可能是相对较重的操作,但实例本身包含的状态非常少,getInstance()init是相对轻量级的操作。因此,创建一些Cipher个实例 - 可能使用相同的密钥 - 对于多个线程来说是好的。

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