近段时间在分析 QQ、微信的数据库,根据网上一些大佬的解密方法,也成功解密了数据。
但是在做自动化处理时,我发现企鹅的生成的 MD5 有时候与我的工具类生成的值相比最前面多个 0
。一开始以为这个 0 有特殊意义,然后为了找到这个“特殊意义”
的依据,对着 DB 数据看了两个晚上。
有特殊意义?
最终,功夫不负有心人,让我找到“特殊意义”。然后欢欢喜喜,一口气撸完几个工具类,测试,通过!
真呀嘛真开心,休息一晚补补,不然发际线跟不上了。
后一晚,公司加班,没搞成!
再后一晚,开始分析微信的 DB,又发现了 MD5 多 0
的问题。嗯,一定也是有特殊意义,找找看……
又一晚,突然想到一个问题,会不会是我的代码有问题?然后仔细对比 MD5,发现一个不科学的现象:
//企鹅的MD5
08711128F207973095353954D9AD22EF
//我的MD5
8711128F207973095353954D9AD22EF
每次企鹅的 MD5 多 0 的时候,可总位数是 32 位
,是 MD5 的长度;而我的只有 31 位,正好少这个 0!!!
PS:不用自己去数数啦,在 AS 里面,选中这个 MD5,
AS 底部
就会显示都少个字符。
这是咋么肥事?
翻出来我的 MD5 的类,是参考 org.apache.commons.codec.digest.DigestUtils
写的,因为这个包直接导入有编译问题,我也只需这个类,所以就参考它重新写了一次,部分地方自己做了“优化”,代码是这样的:
public static String md5Hex(@NonNull final String data) {
return toHex(md5(data));
}
// byte 数组转 16 进制字符串
private static String toHex(final byte[] digest) {
final StringBuilder sb = new StringBuilder();
for (byte t : digest) {
sb.append(Integer.toHexString(t & 0xFF));
}
return sb.toString();
}
//获取加密后的 byte 数组
public static byte[] getDigestBytes(final Algorithm algorithm, @NonNull final byte[] source) {
try {
return MessageDigest.getInstance(algorithm.value()).digest(source);
}
catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
这方法,网上查一下,基本都是类似的写法,理论上不应该有问题的呀。
既然提到上网查,那就查一下吧,果真还不少人碰到过这种问题,但网上类似的问答、文章似乎都是这个页面 (Java使用MD5加密,生成的密文长度只有31位)的盗版。面对那些盗版页面,气的一度想要举报,却发现盗版页面上没有举报入口,简直可恶至极!
在这个问答下,有大佬解释了问什么少 0
:
问题出在这一句:
Integer.toHexString(t & 0xFF)
当 t 为 14 时,十六进制就是0e
,转化成字符串会忽略掉前导零
。
当然,这里说的 t = 14
只是举例,等于其它前面是 0 的十六进制值时,都会出现丢 0 的问题。
解决方法
方法很简单,转成字符串后,加一次判断即可:
private static String toHex(final byte[] digest) {
final StringBuilder sb = new StringBuilder();
for (byte t : digest) {
String s = Integer.toHexString(t & 0xFF);
if (s.length() == 1) {
s = 0 + s;
}
sb.append(s);
}
return sb.toString();
}
用最新修改后的代码测试,果然,少 0 的问题没有了,找了 2 个晚上的“特殊意义”原来是知识量不够。
其它错误写法
在网上还看到过这么写的:
private static String toHex(final byte[] digest) {
return new BigInteger(1, digest).toString(16);
}
怎么样,一行代码就完成了转换,简单明了,是不是很🐂🍺的赶脚?
但是,实际上,这个的内部实现原理类似,因此使用的时候,也会丢 0
。