一尘不染

合并或不合并Java加密服务提供者

java

  • MessageDigest =>根据需要频繁创建新实例
  • KeyFactory =>使用单个共享实例
  • SecureRandom =>使用StackObjectPool
  • 密码=>使用StackObjectPool

在对安全 框架 进行编码时,我经常遇到两难的问题:“ 合并还是不合并”

基本上,这个问题分为两个“组”:

  1. 第1组:SecureRandom因为对的调用nextBytes(...)是同步的,并且可能成为WebApp /多线程应用程序的瓶颈

  2. 第2组:加密服务提供商,如MessageDigestSignatureCipherKeyFactory,…(因为成本的getInstance()?)

你有什么意见 ?

您对这些问题有什么习惯?

编辑09/07/2013

我终于花了一些时间独自测试@Qwerky Share类,我发现结果相当……令人惊讶。

该类缺少我的主要关注:GenericObjectPoolStackObjectPool之类的池。

因此,我对课程进行了重新设计,以测试所有4种选择:

  • 具有同步要点的单个共享实例
  • 每个环内的新实例(我不是在案件感兴趣时,你可以拉环外的摘要生成)要点
  • GenericObjectPool:要点
  • StackObjectPool:要点

由于1M在池中花费了太多时间,因此我不得不将循环数降低到100000。

我还在Thread.yield()每个循环的末尾添加了一个,以使负载具有更好的形状。

结果(累计运行时间):

  • 信息摘要
    • 新实例:420秒
    • 一次实例:550秒
    • StackObjectPool:800秒
    • GenericObjectPool:1900秒
  • 密钥工厂
    • 新实例:400秒
    • 一次实例:350秒
    • StackObjectPool:2900秒
    • GenericObjectPool:3500秒
  • 安全随机
    • StackObjectPool:1600秒
    • 新实例:2300秒
    • GenericObjectPool:2300秒
    • 单实例:2800秒
  • 密码
    • StackObjectPool:2800秒
    • GenericObjectPool:3500秒
    • 单实例:5100秒
    • 新实例:8000秒

结论

对于MessageDigest和KeyFactory而言,池是性能的杀手,甚至比具有同步瓶颈的单个实例还要糟糕,而对于SecureRandom和Cipher来说,它们确实非常有用


阅读 236

收藏
2020-12-03

共1个答案

一尘不染

如果让100个线程访问一个共享,MessageDigest并让它们分别计算1,000,000个哈希,则在我的计算机上,第一个线程完成70,160ms,最后一个完成98,748ms。

如果线程MessageDigest每次都创建一个新实例,那么第一个线程将在43,392ms内完成,最后一个58,691ms。

编辑:
实际上,在此示例中,只有两个线程,该示例创建新实例的运行速度更快。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Share {

  final byte[] bytes = new byte[100];
  final MessageDigest sharedDigest;
  final ExecutorService pool;
  int threads = 100;

  Share() throws NoSuchAlgorithmException {
    sharedDigest = MessageDigest.getInstance("MD5");
    pool = Executors.newFixedThreadPool(threads);
  }

  void go() {

    for (int i=0; i<threads; i++) {
      pool.execute(new Runnable() {
        public void run() {
          long start = System.currentTimeMillis();
          for (int i=0; i<1000000; i++) {
            /*
            synchronized (sharedDigest) {
              sharedDigest.reset();
              sharedDigest.update(bytes);
              sharedDigest.digest();
            }*/
            try {
              MessageDigest digest = MessageDigest.getInstance("MD5");
              digest.reset();
              digest.update(bytes);
              digest.digest();
            } catch (Exception ex) {
              ex.printStackTrace();
            }
          }
          long end = System.currentTimeMillis();
          System.out.println(end-start);
          pool.shutdown();
        }
      });
    }

  }

  public static void main(String[] args) throws Exception {
    Share share = new Share();
    share.go();
  }

}
2020-12-03