2010-11-23 74 views
1

我有包含Java JCA密码和消息摘要对象的实例不可变加密器辅助对象池:这个加密代码线程安全吗?

AlgorithmInstance(Cipher encCipher, Cipher decCipher, MessageDigest digest) { ... } 
private BlockingQueue<AlgorithmInstance> pool = new ArrayBlockingQueue<AlgorithmInstance>(poolSize); 

不同的线程在我的应用程序,需要加密或解密,通过访问池争AlgorithmInstance对象。每个线程使用它们进行加密或解密,然后在完成后将它们返回给池。线程不会在任何JCA对象上同步,因为没有并发访问。解密工作方式大致相同。

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%的时间在单元测试。然而,一旦进入蓝色月亮,我会收到一条消息,其计算的摘要不正确 - 通常这表示消息篡改或网络错误;但是发送者和接收者在同一台机器上(在不同的进程中)。

问:是否有一个密码或文摘一些内部状态,这可以从内存一致性效应的影响 - 我在一个2核心的Windows中,所以我看不出我甚至可以从内存中遭受的一致性效果。我重新初始化密码并消化每个呼叫,所以它应该没有关系。

问:有没有什么办法可以结束与填充模式,有时根据消息长度失败?解密器和加密器使用完全相同的算法(AES/CBC/Pkcs5Padding + SHA-256,密钥大小为128)。

+1

答案1:不是我能看到的。答案2:No. – 2010-11-24 00:50:05

+0

我的感觉是它在GC或者密码/摘要中的一些共享缓冲区有一些副作用;或者@Rook表示我错误地使用了它。它再次发生,文摘完全不同,不仅仅是几个不同点。绝对不知道。 – Justin 2010-11-24 01:30:12

+0

你有没有理由把它们放在一起?它是否真的提供性能优势?我会猜想创建cyper对象不会很昂贵。 – CodesInChaos 2011-01-11 20:58:17

回答

0

我改变了代码,这样我就AlgorithmInstance同步,而我使用它的任何包含的对象。自那以后我没有看到这个问题;然而它不清楚为什么;由于queue.put()queue.take()操作应形成完全一样之前发生由监视器解锁形成关系:

synchronized (inst) { 
... 
// do crypto opperations 
} 

唯一的其他可能性我可以想出的是,IVParamSpec在Cipher.init期间修改()计算,然后在最后恢复。由于ivParams被假定为只读且在池中的所有对象之间共享,因此如果为true,则可能会导致非同步访问并可能导致MCE。

0

encryptMessage()功能自身是线程安全的,因为其不使用存储器2个线程共享。但是,使用此函数的代码可能不是线程安全的。如果要同时对池或队列中的数据进行加密/解密,则可能“当前工作索引”不是以线程安全的方式递增或递减。线程之间共享的全局索引可以通过让互斥锁保护全局整数来创建。在读取此值并更改之前,线程必须请求对此变量进行锁定。

3

你的应用有很多线程吗?因为不是拥有一个池,您可以将您的AlgorithmInstances放入ThreadLocal对象中,从而确保每个AlgorithmInstance始终只由同一个线程使用,所以问题应该消失,您不需要在AlgorithmInstance上同步(所以你会得到更好的表现)。