RSA Cipher的线程安全性

正如标题所说,我想知道在给定RSA算法的情况下如何在multithreading环境中最好地使用Cipher实例。

我已经阅读了关于这个主题的几个问题,以及我收集的内容:

  • 密码不是线程安全的,因为它在加密/解密时保持内部状态

  • 如果使用AES,则doFinal()会将初始化向量重置为上一个已知值,因此每次都应生成新的Cipher实例

我的问题

  • 如果选择的算法是RSA,是否只能调用一次cipher.init() ? 这与第二个子弹相反,如下面的代码所示。 我相信因为没有初始化向量。 此外,密码仅用于解密数据。

  • 我应该只在cipher.doFinal()调用上同步吗?

  • 处理多个线程请求加密服务的常用方法是什么,我应该将密码池作为一些代理后面的阻塞队列吗?

代码示例:

 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); } } 

如果其他人读到这个,就不需要为每次使用RSA的呼叫重新初始化密码。 虽然,密码池可用于提升性能。

我写了快速负载测试来validation这一点。

似乎在cipher.doInit()上同步并使用单个Cipher实例进行解密就足够了。

 private static Queue results = new ConcurrentLinkedQueue(); @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)); } } 

加密(和写消息)本质上是同步的。 使用多个线程不是我的方法。 考虑一条消息aabb。 multithreading可能成为abba或baba或abab或bbaa。 请注意,密码的内部状态也是以这种方式同步…要获得aabb,必须发送aabb。