今天在翻阅jdk源码中加密相关代码时,翻着翻着好像混进来一个奇怪的东西。
正当我以为是普通的equals方法准备划过去时,突然瞥见了一行代码。
result |= digesta[i] ^ digestb[i];
我靠,这是啥东西,这好像和我之前用的equals方法不一样哇,我怀疑我的记忆出现了问题,赶快去看了一下equals的代码。
这肯定不一样好吧,仔细看上面单独拿出来的一行代码,发现MessageDigest类下的isEqual方法是按位比较,直至字符串结束;String类下的Equals类是比较到不相同的字符直接退出。这两段代码一看就知道第一种很费时,既然是比较字符串是否相等,为什么还要第一种呢?
查了一些资料发现,这种可以防止【计时攻击】,计时攻击说白了就是他遍历所有的字符来试你的密码,根据你的响应时间判断前面的几位字符是否相同。比如你的密码是password,你调用euqals遍历一次是1ms,他输入pa666,当你使用第一种isEqual时就是5ms,调用第二种时就是2ms,很容易就能判断出来前两位密码。
写到这里我突然想起来MD5算法,和这个的作用岂不是很像,只不过可能MD5慢了一点。我瞅瞅他能慢到哪去?
String password = "testPassword123456";
long startTime=System.nanoTime();
System.out.println("spring-DigestUtils中md5开始:" + startTime);
String result = DigestUtils.md5DigestAsHex((password).getBytes());
System.out.println(result);
long endTime=System.nanoTime();
System.out.println("spring-DigestUtils中md5结束:" + endTime);
System.out.println("spring-DigestUtils中md5用时:" + (endTime - startTime));
System.out.println("------------------------------------------------------");
long oldStartTime = 0L;
long oldEndTime = 0L;
String oldResult = null;
try {
oldStartTime=System.nanoTime();
System.out.println("jdk-MessageDigest中md5开始:" + oldStartTime);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] byteInput = password.getBytes();
md5.update(byteInput);
byte[] btResult = md5.digest();
StringBuffer stringBuffer = new StringBuffer();
for (byte b : btResult) {
int bt = b&0xff;
if (bt < 16) {
stringBuffer.append(0);
}
stringBuffer.append(Integer.toHexString(bt));
}
oldResult = stringBuffer.toString();
System.out.println(oldResult);
oldEndTime=System.nanoTime();
System.out.println("jdk-MessageDigestmd5结束:" + oldEndTime);
System.out.println("jdk-MessageDigestmd5用时:" + (oldEndTime - oldStartTime));
} catch (Exception e) {
e.printStackTrace();
}
long startMD5Time=System.nanoTime();
System.out.println("md5比较开始:" +startMD5Time);
boolean equals = result.equals(oldResult);
long endMD5Time=System.nanoTime();
System.out.println("md5比较结束:" +endMD5Time);
System.out.println("md5比较用时:" + (endMD5Time - startMD5Time));
System.out.println("spring-DigestUtils中md5总用时:" + (endMD5Time - startMD5Time + (endTime - startTime)));
System.out.println("jdk-MessageDigest中md5总用时:" + (endMD5Time - startMD5Time + (oldEndTime - oldStartTime)));
System.out.println("------------------------------------------------------");
long isEqualStartTime=System.nanoTime();
System.out.println("isEqual开始:" + isEqualStartTime);
boolean equal = MessageDigest.isEqual(password.getBytes(StandardCharsets.UTF_8), password.getBytes(StandardCharsets.UTF_8));
System.out.println("是否相等:" + equal);
long isEqualEndTime=System.nanoTime();
System.out.println("isEqual开始:" + isEqualEndTime);
System.out.println("isEqual用时:" + (isEqualEndTime - isEqualStartTime));
运行结果如下
好吧,jdk中的MD5比isEqual慢了一倍,但是意外的是spring中DigestUtils下的MD5算法竟然慢了这么多,下一期我们解析一下spring中DigestUtils下的MD5算法做了什么,为什么这么慢。
写到这里了笔者还是不理解为啥会有isEqual方法的存在,储存密码的话为什么不直接使用MD5呢?什么情况下会调用这个方法?求指教