我有一个不可变的加密助手对象池,它包含java JCA Cipher和MessageDigest对象的实例:
AlgorithmInstance( Cipher encCipher, Cipher decCipher, MessageDigest digest ) { ... }
private BlockingQueue< AlgorithmInstance > pool = new ArrayBlockingQueue< AlgorithmInstance >(poolSize);
我的应用程序中需要加密或解密的各种线程通过访问池来竞争AlgorithmInstance对象。每个线程使用它们进行加密或解密,然后在完成后将它们返回到池中。由于没有并发访问,线程不会在任何JCA对象上同步。 Decrypt的工作方式大致相同。
public byte[] encryptMessage( byte data[] ) { ...
try {
AlgorithmInstance inst = pool.take();
inst.digest.reset();
byte[] digest = inst.digest.digest(message);
inst.encryptCipher.init( Cipher.ENCRYPT_MODE, m_currentKey, ivParams );
inst.encryptCipher.doFinal( messageBuffer );
}
finally {
pool.put(inst)
}
}
99.99%的时间有效; 100%的单位测试时间。然而,一旦在蓝色的月亮,我得到一条消息,其计算摘要不正确 - 通常这表示消息篡改或网络错误;但发送者和接收者在同一台机器上(在不同的过程中)。
问: Cipher或Digest有一些内部状态可能会受到内存一致性影响 - 我在一个2核的Windows框中,所以我看不出我怎么可能遭受记忆一致性影响。我重新初始化密码并消化每个呼叫,所以它无关紧要。
问:基于邮件长度,有没有办法让填充模式有时失败?解密器和加密器使用完全相同的算法(AES / CBC / Pkcs5Padding + SHA-256,密钥大小为128)。
答案 0 :(得分:3)
你的应用有很多线程吗?因为不是有一个池,你可以将你的AlgorithmInstances放在ThreadLocal对象中,从而确保每个AlgorithmInstance只被同一个线程一直使用,所以问题应该消失,你也不需要同步AlgorithmInstance(所以你会得到更好的表现。)
答案 1 :(得分:0)
encryptMessage()
函数它的self是线程安全的,因为它不使用2个线程共享的内存。但是,使用此函数的代码可能不是线程安全的。如果要同时加密/解密池或队列中的数据,那么“当前工作索引”可能不会以线程安全的方式递增或递减。可以通过具有由互斥锁保护的全局整数来创建线程之间共享的全局索引。在读取并更改此值之前,线程必须请求锁定此变量。
答案 2 :(得分:0)
我更改了代码,以便在使用任何包含的对象时同步AlgorithmInstance。我从未见过这个问题;但不明白为什么;由于queue.put()
和queue.take()
操作应完全形成与监视器解锁形成的发生之前的关系:
synchronized (inst) {
...
// do crypto opperations
}
我能想到的另一个可能性是在Cipher.init()计算期间修改IVParamSpec,然后在结束时恢复。由于ivParams被假定为只读并在池中的所有对象之间共享,如果为true,则可能导致不同步的访问,并可能导致MCE。