折腾:
【已解决】为何Python中32字节的md值和小花生中getMD5Str计算出的md5值不同
期间,看了些资料后感觉是
java中的md5值,计算出来,不是原以为的32位,而是16位啊
注:此处的位,不是bit,而是字节
所以需要去搞清楚java中md5值计算出来到底是16位还是32位的
java md5 16 or 32
md5.digest() is 16 bytes.
也给出了16变32位的写法:
public static String toHexString(byte[] bytes) {
if (bytes == null) {
throw new IllegalArgumentException("byte array must not be null");
}
StringBuffer hex = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
hex.append(Character.forDigit((bytes[i] & 0XF0) >> 4, 16));
hex.append(Character.forDigit((bytes[i] & 0X0F), 16));
}
return hex.toString();
}所以现在问题转换为:
如何用Python实现,java中的32字节的md5的计算法:
先用java的java.security.MessageDigest的getInstance(“MD5”),算出16字节的md5.digest
然后再去处理合并成32位的
另外:如果要返回16位的,用的是buf.toString().substring(8, 24)(而不是最开始计算出来的16位)
-》此处的:
char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};貌似就是此处java中的:
char[] arrayOfChar = new char[16]; char[] tmp6_5 = arrayOfChar; tmp6_5[0] = 48; char[] tmp12_6 = tmp6_5; tmp12_6[1] = 49; char[] tmp18_12 = tmp12_6; tmp18_12[2] = 50; char[] tmp24_18 = tmp18_12; tmp24_18[3] = 51; char[] tmp30_24 = tmp24_18; tmp30_24[4] = 52; char[] tmp36_30 = tmp30_24; tmp36_30[5] = 53; char[] tmp42_36 = tmp36_30; tmp42_36[6] = 54; char[] tmp49_42 = tmp42_36; tmp49_42[7] = 55; char[] tmp56_49 = tmp49_42; tmp56_49[8] = 56; char[] tmp63_56 = tmp56_49; tmp63_56[9] = 57; char[] tmp70_63 = tmp63_56; tmp70_63[10] = 97; char[] tmp77_70 = tmp70_63; tmp77_70[11] = 98; char[] tmp84_77 = tmp77_70; tmp84_77[12] = 99; char[] tmp91_84 = tmp84_77; tmp91_84[13] = 100; char[] tmp98_91 = tmp91_84; tmp98_91[14] = 101; char[] tmp105_98 = tmp98_91; tmp105_98[15] = 102; tmp105_98;
?
参考自己整理的ASCII码表
果然是:
字符0是48 -》 9是57
97是小写的a -》 102是小写的f
好像和上面不完全一样:
上面的是大写的A(65)到大写的F(70)
难道此处是A到F,大小写无关?
突然发现了:
对于:
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf];
的确没区别-》因为这是得到A到F的字符而已,大写还是小写,无所谓
-》因为输出的32位的字符,一般是不区分大小写的。
【总结】
所以至此基本上明白了:
java中的md5.digest()输出是16个字节(中文中,不严谨的叫法,叫做16位)
而对于看到的32字节的值,是处理合并出来的。
具体逻辑是:
(参考
中代码写的逻辑最清晰)
import java.math.BigInteger;
import java.security.MessageDigest;
public class AppMD5Util {
/**
* 对字符串md5加密(小写+字母)
*
* @param str 传入要加密的字符串
* @return MD5加密后的字符串
*/
public static String littleMD5a(String str) {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes());
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 16位是将32位中的16截取出来
public static String little16MD5a(String str) {
return littleMD5a(str).substring(8, 24);
}
/**
* 对字符串md5加密(大写+数字)
*
* @param str 传入要加密的字符串
* @return MD5加密后的字符串
*/
public static String BigMD55(String string) {
char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
try {
byte[] btInput = string.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 16位是将32位中的16截取出来
public static String Big16MD55(String str) {
return BigMD55(str).substring(8, 24);
}
...
}16位转32位的核心逻辑是:
return new BigInteger(1, md.digest()).toString(16);
-》上面这个没太懂
或:
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}-》上面这个基本搞懂了:
-》和此处要研究的java代
paramString = paramString.getBytes();
Object localObject = MessageDigest.getInstance("MD5");
((MessageDigest)localObject).update(paramString);
paramString = ((MessageDigest)localObject).digest();
int i = paramString.length;
localObject = new char[i * 2];
int j = 0;
int k = 0;
while (j < i)
{
int m = paramString[j];
int n = k + 1;
localObject[k] = ((char)arrayOfChar[(m >> 4 & 0xF)]);
k = n + 1;
localObject[n] = ((char)arrayOfChar[(m & 0xF)]);
j++;
}
paramString = new String((char[])localObject);
return paramString;
}以及网上其他代码,都是一样的处理逻辑
-》且由于输出的32字节的值中的(a到f的)字符大小写都允许
-》所以被用来处理所涉及的字符串数组中的a-f字符是大写还是小写,都无所谓的:
-》
即:
hexDigits中大写的A-F:
char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};还是
arrayOfChar中的小写a-f:
char[] arrayOfChar = new char[16]; char[] tmp6_5 = arrayOfChar; tmp6_5[0] = 48; char[] tmp12_6 = tmp6_5; tmp12_6[1] = 49; char[] tmp18_12 = tmp12_6; tmp18_12[2] = 50; char[] tmp24_18 = tmp18_12; tmp24_18[3] = 51; char[] tmp30_24 = tmp24_18; tmp30_24[4] = 52; char[] tmp36_30 = tmp30_24; tmp36_30[5] = 53; char[] tmp42_36 = tmp36_30; tmp42_36[6] = 54; char[] tmp49_42 = tmp42_36; tmp49_42[7] = 55; char[] tmp56_49 = tmp49_42; tmp56_49[8] = 56; char[] tmp63_56 = tmp56_49; tmp63_56[9] = 57; char[] tmp70_63 = tmp63_56; tmp70_63[10] = 97; char[] tmp77_70 = tmp70_63; tmp77_70[11] = 98; char[] tmp84_77 = tmp77_70; tmp84_77[12] = 99; char[] tmp91_84 = tmp84_77; tmp91_84[13] = 100; char[] tmp98_91 = tmp91_84; tmp98_91[14] = 101; char[] tmp105_98 = tmp98_91; tmp105_98[15] = 102; tmp105_98;
其中
(48-57是数字字符0-9)
97-102对应着字符小写的a-f
接着问题就转化为:
【未解决】用Python实现Java中的32位(字节)的md5的计算逻辑即先md5.digest生成16位再合并处理成32位
【后记20190403】
后来从
【已解决】用jadx把安卓dex文件转换提取出jar包和java源代码
的jadx导出的java源码中,看到逻辑更加清晰的java代码:

public static final String getMD5Str(String str) {
char[] cArr = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] bytes = str.getBytes();
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(bytes);
char[] cArr2 = new char[(r1 * 2)];
int i = 0;
for (byte b : instance.digest()) {
int i2 = i + 1;
cArr2[i] = cArr[(b >> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception unused) {
return null;
}
}->很明显,此处jadx已经可以完美的转换出原始的java源代码,其中有我们,之前推断出来的字符数组
char[] cArr = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};了,而无需自己反向推断了。
注:
(1)md5无法反向解密
“Md5加密方式不能反向解密”
-》不知道,这里:
md5在线解密破解,md5解密加密
还弄个解密的,是啥意思
且自己试了试,也的确无法解密。不知道网站想干啥。
(2)举例:md5的32字节中提取16位是8-24位的数据
给的例子:
MD5(sunzn,32) = 40379db889f9124819228947faaeb1f7 MD5(sunzn,16) = 89f9124819228947
-》可以看出16字节的值:89f9124819228947
就是32字节的值:40379db889f9124819228947faaeb1f7
的8-24的16个字节
-》所以之前代码都是写成:
littleMD5a(str).substring(8, 24);
去获取16字节的值的。