Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 333
Aman Avatar asked May 15 '17 22:05

Aman


2 Answers

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

like image 176
Maarten Bodewes Avatar answered Sep 28 '22 05:09

Maarten Bodewes


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.

like image 26
Artjom B. Avatar answered Sep 28 '22 05:09

Artjom B.