MessageDigest的并发问题

作者: wencst 分类: JAVA,程序设计 发布时间: 2021-08-24 11:38 阅读: 2,592 次

问题描述

在程序中采用了静态变量的方式实例化了MessageDigest

    private static MessageDigest CRYPT = null;

    static {
        try {
            CRYPT = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            log.warn("Failed to get sha-1 message digest.");
        }
    }

然后采用SHA1进行加密

 

    private String encryptSHA1(String key) {
        String sha1 = "";
        CRYPT.reset();
        //1
        CRYPT.update((key + SALT).getBytes(StandardCharsets.UTF_8));
        //2         sha1 = byteToHex(CRYPT.digest());
        return sha1; 
    }

SALT目前是固定值,当调用encryptSHA1时,偶尔会返回错误的加密值。

实验验证

首先,SHA1的加密方式是每次加密结果都一致。所以猜测是并发引起的。下面实验验证一下。

实验一:

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            System.out.println(service.encryptSHA1("12345678"));
            System.out.println(service.encryptSHA1("87654321"));
            Thread.sleep(100);
        }
    }

结果没有问题。

 

实验二:

    public static void main(String[] args) {
        Thread[] ts = new Thread[100];
        for (int i = 0;i<100;i++) {
            Thread t = new Thread(String.valueOf(new Random().nextLong())) {
                public void run() {
                    service.encryptSHA1(Thread.currentThread().getName());
                }
            };
            ts[i] = t;
        }
        for (int i = 0;i<100;i++) {
            Thread t = ts[i];
            t.start();
        }
    }

这个结果出问题了,当并发的时候,加密的结果偶尔是上一个加密的结果。

实验三:

    private String encryptSHA1(String key) {
        String sha1 = byteToHex(DigestUtils.sha1((key + SALT).getBytes(StandardCharsets.UTF_8)));
        return sha1;
    }

再次使用实验二中的main方法验证,结果正常。

 

分析问题

    private String encryptSHA1(String key) {
        String sha1 = "";
        CRYPT.reset();
        //1
        CRYPT.update((key + SALT).getBytes(StandardCharsets.UTF_8));
        //2
        sha1 = byteToHex(CRYPT.digest());
        return sha1;
    }

由于CRYPT是静态变量,只初始化一次,所以多个线程使用的是同一个对象,当并发比较大时,会出现A线程执行完//1还没有执行//2时,B线程执行完//1,此时A线程执行//2,会显示B线程的结果。

而DigestUtils实际使用的是每次调用 crypt = MessageDigest.getInstance(“SHA-1”); 验证每次生成的crypt都是新的对象。

因此我们也可以自己实现DigestUtils的sha1方法。

如果文章对您有用,扫一下支付宝的红包,不胜感激!

欢迎加入QQ群进行技术交流:656897351(各种技术、招聘、兼职、培训欢迎加入)



Leave a Reply