In a multi threaded Java application, we are using AES-256 for encryption and decryption of files to the disk. Please note that multiple threads can make concurrent calls to the encryption and decryption methods for different files.
Encryption:
Cipher encrypter = Cipher.getInstance(algorithm, new BouncyCastleProvider());
IvParameterSpec ivSpec = getIvParamSpec(encrypter.getBlockSize());
encrypter.init(Cipher.ENCRYPT_MODE, key, ivSpec);
//..encrypt the data
Decryption:
Cipher decrypter = Cipher.getInstance(algorithm, new BouncyCastleProvider());
IvParameterSpec ivSpec = readIvParamSpec(decrypter.getBlockSize(), is);
decrypter.init(Cipher.DECRYPT_MODE, key, ivSpec);
//.. decrypt the data
In our understanding, it is better to use random IV for encryption instead of static/fixed IV. For this purpose, we are using SecureRandom API to generate the IV. The random IV is persisted in the encrypted file at the start.
SecureRandom random = new SecureRandom();
byte[] iv = new byte[ivSizeBytes];
random.nextBytes(iv);
new IvParameterSpec(iv);
My question is, since I am using random IV for each encryption, do I need to call Cipher.getInstance() and Cipher.Init() for all the calls? For performance improvement, can these be called only once during the class initialization and then reuse the individual cipher instances to encrypt and decrypt the data?
Thanks in advance!
My question is, since I am using random IV for each encryption, do I need to call Cipher.getInstance() and Cipher.Init() for all the calls?
You can reuse Cipher instances as long as you don't share them among threads. As Artjom mentioned, Cipher instances are stateful. They store both the IV but also the last ciphertext (used as next vector for CBC mode encryption) and possibly some buffered plaintext. Mixing that state with the input from different threads will result in chaos.
As you need a new random for each file encryption you do need to call init
again after calling a doFinal
method. Init is not that heavyweight; the one thing that can take a bit of performance is the subkey derivation for AES, but generally that's not a big issue.
Note that, although performing the encryption and decryption can be relatively heavy weight operations, the instances themselves contain very little state and getInstance()
and init
are relatively lightweight operations. So creating a few more Cipher
instances - possibly with the same key - is fine for multiple threads.
Recreating the BouncyCastleProvider
multiple times is a very bad idea, even though it probably uses some kind of singleton underneath. But basically you don't need the Java only Bouncy Castle implementation. The Oracle one may use AES-NI intrinsics that will directly use the AES-NI instruction set on compatible processors. That will run circles around Bouncy Castle - expect a speedup of around 7 to 13 times (!).
Yes, you need to use a different Cipher
instance for each thread, because they are stateful. If you don't, then threads can break the ciphertext of other threads.
Let's assume we have two threads t1
and t2
which want to encrypt two plaintexts p1_1 | p1_2
and p2_1 | p2_1
(split on block boundary). Let's take CBC as an example:
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
is ok, but c2_1
is not ok, because the state from t1
was used for start the encryption. It is as though the encryption was initialized with c1_1
as the IV.
This example works only for CBC mode, but other modes of operation are similar. If we assume that encryption happens only block-wise, then you could just use ECB mode in a thread-safe fashion, but this is only an illusion, because you cannot be sure that the implementation only handles the internal state block-wise and not byte-wise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With