Stream Security with RSA Cipher

As the headers say, I would like to know how best to use the Cipher instance in a multi-threaded environment, taking into account the RSA algorithm.

I read a couple of questions on this topic and from what I have compiled:

  • The cipher is not thread safe because it maintains an internal state when encrypting / decrypting

  • if AES is used, then doFinal() will reset the initialization of the vector to the last known value, and therefore a new Cipher instance must be generated every time

My questions

  • Is it possible to call cipher.init() only once if the selected RSA algorithm? This contradicts the second bullet, as shown in the following code. I believe that there is no initialization vector. In addition, the cipher will only be used to decrypt data.

  • Should I sync only when cipher.doFinal() called?

  • What is the general way to handle multiple threads requesting cryptography services, should I have a cipher pool as a blocking queue for some proxy?

Code example:

 public class RsaPrototype { private static PrivateKey privKey; private static Cipher cipher; private static final String PRIVATE_KEY_PATH ="./privK.pem"; /* * ALGORITHM/BLOCKING_MODE/PADDING_SCHEMA */ private static final String CIPHER_SPECIFICATION = "RSA/None/NoPadding"; private static final String RSA_PROVIDER ="BC"; static { Security.addProvider(new BouncyCastleProvider()); importPrivateKey(); cipher = getCipher(); } /** * Initializes cipher with RSA algorithm, without blocking mode and padding. * Implementation provider is bouncy castle. * * @return cipher instance. */ private static Cipher getCipher() { try { Cipher cipher = Cipher.getInstance(CIPHER_SPECIFICATION, RSA_PROVIDER); cipher.init(Cipher.DECRYPT_MODE, privKey); return cipher; } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException e) { throw new RuntimeException(e.getMessage()); } } /** * Imports public key from the given .PEM file into application cache. */ private static void importPrivateKey() { try (BufferedReader reader = new BufferedReader(new FileReader(PRIVATE_KEY_PATH)); PEMParser pemParser = new PEMParser(reader);) { privKey = new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo) pemParser.readObject()); } catch (IOException ignorable) { // not handled } } public static String decrypt(byte[] encryptedText) { byte[] plainText; synchronized (cipher) { plainText = cipher.doFinal(encryptedText); } return new String(plainText, StandardCharsets.UTF_8); } } 
+4
source share
2 answers

If someone else reads this, there will be no need to reinitialize the cipher for each call using RSA. Although the cipher pool can be used to improve performance.

I checked the quick boot to check this out.

It seems that it is enough to synchronize on cipher.doInit() and use one instance of Cipher for decryption.

 private static Queue<String> results = new ConcurrentLinkedQueue<String>(); @Test public void test() throws InterruptedException { String plainText = "some text to verify data integrity"; String encryptedText = Encrypt.encrypt(plainText); for (int i = 0; i < 5000; i++) { new Thread( () -> { results.add(Decrypt.decrypt(encryptedText)); }) .start();; } Thread.sleep(5000); assertTrue(results.size() == 5000); while(!results.isEmpty()) { assertTrue(results.poll().equals(plainText)); } } 
+2
source

Encryption (and message recording) is inherently synchronous. Using multiple threads would not be my approach. Consider the aabb post. With multiple threads that can become abba or baba or abab or bbaa. Note that the internal state of the cipher is also synchronous ... To receive aabb, you must send aabb.

+1
source

All Articles