最新消息:20210917 已从crifan.com换到crifan.org

【已解决】java中md5值的长度是16位(字节)还是32位(字节)及两者之间关系

Java crifan 1244浏览 0评论
折腾:
【已解决】为何Python中32字节的md值和小花生中getMD5Str计算出的md5值不同
期间,看了些资料后感觉是
java中的md5值,计算出来,不是原以为的32位,而是16位啊
注:此处的位,不是bit,而是字节
所以需要去搞清楚java中md5值计算出来到底是16位还是32位的
java md5 16 or 32
Android、Java中16位及32位MD5加密算法工具类,直接使用!!! – 我是程序猿,我为自己代言 – CSDN博客
java中16位或32位MD5加密 – u010197591的专栏 – CSDN博客
java中MD5 16和32位加密 | 王柏元的博客
md5 – Java MessageDigest providing 31 bit instead of 32 bit – Stack Overflow
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位)
Java 编程下字符串的 16 位、32位 MD5 加密 – sunzn – 博客园
MD5、SHA1加密java 16位32位 – 清风软件测试 – 博客园
-》此处的:
      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码表
https://www.crifan.org/files/doc/docbook/char_encoding/release/html/char_encoding.html#ascii_table_0_127
果然是:
字符0是48 -》 9是57
97是小写的a -》 102是小写的f
好像和上面不完全一样:
上面的是大写的A(65)到大写的F(70)
难道此处是A到F,大小写无关?
突然发现了:
对于:
https://www.cnblogs.com/111testing/p/9944284.html
  str[k++] = hexDigits[byte0 >>> 4 & 0xf];
  str[k++] = hexDigits[byte0 & 0xf];
的确没区别-》因为这是得到A到F的字符而已,大写还是小写,无所谓
-》因为输出的32位的字符,一般是不区分大小写的。
【总结】
所以至此基本上明白了:
java中的md5.digest()输出是16个字节(中文中,不严谨的叫法,叫做16位)
而对于看到的32字节的值,是处理合并出来的。
具体逻辑是:
(参考
https://www.cnblogs.com/111testing/p/9944284.html
中代码写的逻辑最清晰)
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无法反向解密
java中MD5 16和32位加密 | 王柏元的博客
“Md5加密方式不能反向解密”
-》不知道,这里:
md5在线解密破解,md5解密加密
https://www.cmd5.com
还弄个解密的,是啥意思
且自己试了试,也的确无法解密。不知道网站想干啥。
(2)举例:md5的32字节中提取16位是8-24位的数据
Java 编程下字符串的 16 位、32位 MD5 加密 – sunzn – 博客园
给的例子:
MD5(sunzn,32) = 40379db889f9124819228947faaeb1f7
MD5(sunzn,16) = 89f9124819228947
-》可以看出16字节的值:89f9124819228947
就是32字节的值:40379db889f9124819228947faaeb1f7
的8-24的16个字节
-》所以之前代码都是写成:
littleMD5a(str).substring(8, 24);
去获取16字节的值的。

转载请注明:在路上 » 【已解决】java中md5值的长度是16位(字节)还是32位(字节)及两者之间关系

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
99 queries in 0.179 seconds, using 23.40MB memory