代码之家  ›  专栏  ›  技术社区  ›  Justin

这个加密代码是线程安全的吗?

  •  1
  • Justin  · 技术社区  · 14 年前

    我有一个不可变的加密助手对象池,其中包含JavaJCA密码和MigaGeDigestObjts对象的实例:

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

    我的应用程序中需要加密或解密的各种线程通过访问池来争夺算法实例对象。每个线程使用它们来加密或解密,然后在完成后将它们返回到池中。线程不会在任何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)。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Chochos    14 年前

    你的应用程序有很多线程吗?因为您可以将AlgorithmInstances放在线程本地对象中,而不是使用池,从而确保每个AlgorithmInstance始终只由同一线程使用,因此问题应该消失,您也不需要在AlgorithmInstance上同步(这样您将获得更好的性能)。

        2
  •  0
  •   rook    14 年前

    这个 encryptMessage() 函数本身是线程安全的,因为它不使用由两个线程共享的内存。但是,使用此函数的代码可能不是线程安全的。如果要同时加密/解密池或Que中的数据,那么“当前工作索引”可能不会以线程安全的方式递增或递减。线程之间共享的全局索引可以由互斥体保护的全局整数创建。在读取并更改此值之前,线程必须请求锁定此变量。

        3
  •  0
  •   Justin    14 年前

    我更改了代码,以便在使用算法实例中包含的任何对象时在该实例上进行同步。从那以后,我就没有见过这个问题;但不清楚为什么;从那以后 queue.put() queue.take() 操作的形式应该完全相同 以前发生过 监视器解锁形成的关系:

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

    我唯一能想到的另一种可能性是,在cipher.init()计算期间修改了ivparamspec,然后在最后恢复。由于假设ivparams是只读的,并且在池中的所有对象之间共享,如果为真,这可能会导致不同步的访问,可能还会导致mce。