折腾:
【已解决】为何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字节的值的。