MessageDigest的并发问题
问题描述
在程序中采用了静态变量的方式实例化了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方法。