Cipher.getInstance() and Cipher.getInit() for each message in case of random IV for AES encryption
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.
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 (!).