Search code examples
javaencryptionaes

Cipher.getInstance() and Cipher.getInit() for each message in case of random IV for AES encryption


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!


Solution

  • 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 (!).