C#.NET与JAVA互通之MD5哈希V2024

  • C#.NET与JAVA互通之MD5哈希V2024已关闭评论
  • 19 次浏览
  • A+
所属分类:.NET技术
摘要

C#.NET与JAVA互通之MD5哈希V2024 配套视频:  要点:1.计算MD5时,SDK自带的计算哈希(ComputeHash)方法,输入输出参数都是byte数组。就涉及到字符串转byte数组转换时,编码选择的问题。

C#.NET与JAVA互通之MD5哈希V2024

 

配套视频:

 

 

要点:

1.计算MD5时,SDK自带的计算哈希(ComputeHash)方法,输入输出参数都是byte数组。就涉及到字符串转byte数组转换时,编码选择的问题。

2.输入参数,字符串转byte数组时,编码双方要统一,一般为:UTF-8。

3.输出参数,byte数组转字符串时,编码双方要统一,一般为:16进制字符串(注意大小写);也有人选择BASE64字符串。

4.如果你的MD5用于存储密码,最好要加盐。

5.MD5用于签名,常见的运算过程。

 

开整:

一、.NET 算MD5

1.常用.NET MD5 代码:

string orgKey = "HelloWorld"; Console.WriteLine("待哈希字符串:" + orgKey); MD5 md = MD5.Create(); byte[] bytes = Encoding.UTF8.GetBytes(orgKey); byte[] buffer2 = md.ComputeHash(bytes); string str = ""; for (int i = 0; i < buffer2.Length; i++) {     str = str + buffer2[i].ToString("x2"); } Console.WriteLine("哈希后转16进制小写:" + str);

输出效果(小写x):

待哈希字符串:HelloWorld 哈希后转16进制小写:68e109f0f40ca72a15e05cc22786f8e6

注意:.ToString("x2") 里面的x是小写,即输出16进制小写。

C#.NET与JAVA互通之MD5哈希V2024

 

 如果X是大写,则输出16进制大写字符串。代码如下:

str = ""; for (int i = 0; i < buffer2.Length; i++) {     str = str + buffer2[i].ToString("X2"); } Console.WriteLine("哈希后转16进制大写:" + str);

输出效果(大写X):

待哈希字符串:HelloWorld 哈希后转16进制大写:68E109F0F40CA72A15E05CC22786F8E6

 

如果不使用.ToString("x2") ,我们还可以使用BitConverter来转换为16进制字符串。

但BitConverter默认转出的字符串是大写并带“-”符号。代码如下:

string orgKey = "HelloWorld"; Console.WriteLine("待哈希字符串:" + orgKey); MD5 md = MD5.Create(); byte[] bytes = Encoding.UTF8.GetBytes(orgKey); byte[] buffer2 = md.ComputeHash(bytes); string str = ""; str = BitConverter.ToString(buffer2); Console.WriteLine("哈希后用BitConverter转16进制原始:" + str);

BitConverter.ToString 默认输出效果:

待哈希字符串:HelloWorld 哈希后用BitConverter转16进制原始:68-E1-09-F0-F4-0C-A7-2A-15-E0-5C-C2-27-86-F8-E6 结束 。

我们BitConverter.ToString转小写,并不带“-”符号。代码如下:

string orgKey = "HelloWorld"; Console.WriteLine("待哈希字符串:" + orgKey); MD5 md = MD5.Create(); byte[] bytes = Encoding.UTF8.GetBytes(orgKey); byte[] buffer2 = md.ComputeHash(bytes); string str = "";  str = BitConverter.ToString(buffer2).Replace("-", "").ToLower(); Console.WriteLine("哈希后用BitConverter转16进制小写:" + str);

效果:

待哈希字符串:HelloWorld 哈希后用BitConverter转16进制小写:68e109f0f40ca72a15e05cc22786f8e6 结束 。

 

 

二、JAVA MD5

JAVA 算MD5代码:

 

public static void main( String[] args )     {         try         {             String input = "HelloWorld";             String md5Hash = getMD5Hash(input);             System.out.println("待哈希字符串 '" + input + "' 16进制小写: " + md5Hash);         }catch (Exception ex){             System.out.println( "ex!"+ex.getMessage() );         }         System.out.println( "结束!" );     }      public static String getMD5Hash(String input) {         try {             byte[] byInput=input.getBytes();//默认是UTF-8             // 获取MD5 MessageDigest实例             MessageDigest md = MessageDigest.getInstance("MD5");             // 更新要计算哈希的输入             md.update(byInput);             // 完成哈希计算并返回             byte[] digest = md.digest();             // 将字节数组转换为16进制的字符串             StringBuilder sb = new StringBuilder();             for (byte b : digest) {                 sb.append(String.format("%02x", b & 0xff));             }             // 转换为小写(如果需要)             return sb.toString().toLowerCase();         } catch (NoSuchAlgorithmException e) {             throw new RuntimeException("MD5 not supported", e);         }     }

 

 

输出效果:

待哈希字符串 'HelloWorld' 16进制小写: 68e109f0f40ca72a15e05cc22786f8e6 结束!

C#.NET与JAVA互通之MD5哈希V2024

 

 

三、同一个字符串 .NET MD5 与 JAVA 是否一致

双方待哈希字符串都为“HelloWorld”,且转16进制小写。将.NET算得的结果,放到JAVA中进行比较:

String input = "HelloWorld";             String javaMd5Hash = getMD5Hash(input);             System.out.println("JAVA算出的MD5值:" + javaMd5Hash  );             String netStr="68e109f0f40ca72a15e05cc22786f8e6";             System.out.println(".NET算出的MD5值:" + netStr  );             boolean bPP=netStr.equals(javaMd5Hash);             System.out.println("两者是否匹配:" + bPP  );

效果:

JAVA算出的MD5值:68e109f0f40ca72a15e05cc22786f8e6 .NET算出的MD5值:68e109f0f40ca72a15e05cc22786f8e6 两者是否匹配:true 结束!

 

 

四、MD5加盐

假设你的登录名为:HelloWorld,密码是:123456,密码用MD5 HASH后存储,未加盐,数据库被脱库,对方得到了HelloWorld对应的密码存储MD5值:e10adc3949ba59abbe56e057f20f883e。

对方就可以用撞库,即:计算从 100000 到 999999 之间的MD5 HASH值,每次算出MD5值后,与e10adc3949ba59abbe56e057f20f883e比对,如果相等,则反推出HelloWorld的密码。

代码模拟:

static void Main(string[] args) {     try     {         string targetMd5 = "e10adc3949ba59abbe56e057f20f883e";         Console.WriteLine("目标MD5 值:" + targetMd5);         for (int i = 100000; i <= 999999; i++)         {             string tmpMd5 = GetMd5(i.ToString());             if (tmpMd5 == targetMd5) {                 Console.WriteLine("MD5 已匹配,对应密码:" + i.ToString());                 break;             }         }     }     catch (Exception ex)     {         Console.WriteLine("ex:"+ex.Message);     }     Console.WriteLine("结束 。"  );     Console.ReadKey(); }  static string GetMd5(string orgKey) {                  MD5 md = MD5.Create();     byte[] bytes = Encoding.UTF8.GetBytes(orgKey);     byte[] buffer2 = md.ComputeHash(bytes);                 string str = BitConverter.ToString(buffer2).Replace("-", "").ToLower();     return str; }

效果:

目标MD5 值:e10adc3949ba59abbe56e057f20f883e MD5 已匹配,对应密码:123456 结束 。

C#.NET与JAVA互通之MD5哈希V2024

所谓加盐,就是将原始字符串的头部或尾部加上一段固定的字符串,然后再去算MD5值。大致代码如下:

static void Main(string[] args) {     try     {         string orgStr = "123456";         Console.WriteLine("原始字符串:" + orgStr);         string salt = "salt10086";//硬编码或写配置文件中,一种场景固定一个盐值。         Console.WriteLine("盐字符串:" + salt);         string md5NoSalt = GetMd5(orgStr);         Console.WriteLine("原始字符串:" + orgStr+ "不加盐:"+ md5NoSalt);         string orgWithSalt = orgStr + salt;//盐值拼接在头部还是尾部,自行决定         Console.WriteLine("原始字符串与盐拼接后的字符串:" + orgWithSalt);         string md5WithSalt = GetMd5(orgWithSalt);         Console.WriteLine("原始字符串:" + orgStr + "加盐:" + md5WithSalt);     }     catch (Exception ex)     {         Console.WriteLine("ex:"+ex.Message);     }     Console.WriteLine("结束 。"  );     Console.ReadKey(); }  static string GetMd5(string orgKey) {                  MD5 md = MD5.Create();     byte[] bytes = Encoding.UTF8.GetBytes(orgKey);     byte[] buffer2 = md.ComputeHash(bytes);                 string str = BitConverter.ToString(buffer2).Replace("-", "").ToLower();     return str; }

效果:

原始字符串:123456 盐字符串:salt10086 原始字符串:123456不加盐:e10adc3949ba59abbe56e057f20f883e 原始字符串与盐拼接后的字符串:123456salt10086 原始字符串:123456加盐:6ff7189064eea9523e3814d440ec6adc 结束 。

不加盐:e10adc3949ba59abbe56e057f20f883e,加盐:6ff7189064eea9523e3814d440ec6adc,明显不一致。

如果你的密码加了盐,对方不仅要脱库,还要知道你的盐是多少,盐是如何拼接的,才能撞库出原密码。

 

五、常用MD5签名算法

.NET注意:对KEY排序时,无论是Array.Sort,还是SortedDictionary,必须要加 string.CompareOrdinal 参数,不然默认不区分大小写,和JAVA默认区分大小写的行为不一致,导致双方签名不一致。

运算过程:

1.准备一个键值对集合,

2.集合的键按ASCII 从小到大排序,

3.使用&和=拼接,

4.算MD5哈希,注意对方要求的大小写。

.NET代码:

static void Main(string[] args) {     try     {         //D、a、E三者的ASCII码分别为68、97、69         //1.准备一个键值对集合         Dictionary<string,string> dic1 = new Dictionary<string,string>();         dic1.Add("Dip4","10086");         dic1.Add("aip3", "10000");         dic1.Add("Eip2", "10010");         Console.WriteLine("1.准备一个键值对集合");         foreach (string key in dic1.Keys) {             Console.WriteLine("key:"+ key+" value:"+ dic1[key]);         }         Console.WriteLine("2.集合的键按ASCII排序");         IDictionary<string, string> dic2=HashUtil.AsciiDictionary(dic1);                         foreach (string key in dic2.Keys)         {             Console.WriteLine("key:" + key + " value:" + dic2[key]);         }         Console.WriteLine("3.使用&和=拼接");         string finalStr=HashUtil.BuildQueryString(dic2);         Console.WriteLine("拼接后的字符串"+ finalStr);         Console.WriteLine("4.算MD5哈希,注意对方要求的大小写");         string md5Str = GetMd5(finalStr);         Console.WriteLine("MD5哈希:" + md5Str);     }     catch (Exception ex)     {         Console.WriteLine("ex:"+ex.Message);     }     Console.WriteLine("结束 。"  );     Console.ReadKey(); }  static string GetMd5(string orgKey) {                  MD5 md = MD5.Create();     byte[] bytes = Encoding.UTF8.GetBytes(orgKey);     byte[] buffer2 = md.ComputeHash(bytes);                 string str = BitConverter.ToString(buffer2).Replace("-", "").ToLower();     return str; }

 

HashUtil 工具类:

using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text;  namespace CommonUtils {     /// <summary>     /// 工具类,runliuv,2024-06-12     /// </summary>     public static class HashUtil     {          public static string GetMd5(string src)         {             MD5 md = MD5.Create();             byte[] bytes = Encoding.UTF8.GetBytes(src);             byte[] buffer2 = md.ComputeHash(bytes);             string str = "";             for (int i = 0; i < buffer2.Length; i++)             {                 str = str + buffer2[i].ToString("x2");             }             return str;          }          public static IDictionary<string, string> ModelToDic<T1>(T1 cfgItem)         {             IDictionary<string, string> sdCfgItem = new Dictionary<string, string>();              System.Reflection.PropertyInfo[] cfgItemProperties = cfgItem.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);             foreach (System.Reflection.PropertyInfo item in cfgItemProperties)             {                 string name = item.Name;                 object value = item.GetValue(cfgItem, null);                 if (value != null && (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String")) && !string.IsNullOrWhiteSpace(value.ToString()))                 {                     sdCfgItem.Add(name, value.ToString());                 }             }              return sdCfgItem;         }          public static IDictionary<string, string> AsciiDictionary(IDictionary<string, string> sArray)         {             IDictionary<string, string> asciiDic = new Dictionary<string, string>();             string[] arrKeys = sArray.Keys.ToArray();             Array.Sort(arrKeys, string.CompareOrdinal);             foreach (var key in arrKeys)             {                 string value = sArray[key];                 asciiDic.Add(key, value);             }             return asciiDic;         }          public static string BuildQueryString(IDictionary<string, string> sArray)         {              //拼接 K=V&A=B&c=1 这种URL              StringBuilder sc = new StringBuilder();              foreach (var item in sArray)             {                 string name = item.Key;                 string value = item.Value;                 if (!string.IsNullOrWhiteSpace(value))                 {                     sc.AppendFormat("{0}={1}&", name, value);                 }              }              string fnlStr = sc.ToString();             fnlStr = fnlStr.TrimEnd('&');              return fnlStr;         }      } }

 

 

.NET效果:

1.准备一个键值对集合 key:Dip4 value:10086 key:aip3 value:10000 key:Eip2 value:10010 2.集合的键按ASCII排序 key:Dip4 value:10086 key:Eip2 value:10010 key:aip3 value:10000 3.使用&和=拼接 拼接后的字符串Dip4=10086&Eip2=10010&aip3=10000 4.算MD5哈希,注意对方要求的大小写 MD5哈希:35dffab906c15e2f9d7d1f60c8817584

 

JAVA代码:

public static void main( String[] args )     {         try         {             // 创建一个未排序的Map             Map<String, String> unsortedMap = new HashMap<>();             unsortedMap.put("Dip4", "10086");             unsortedMap.put("aip3", "10000");             unsortedMap.put("Eip2", "10010");             // 创建一个新的TreeMap来保存排序后的键值对             Map<String, String> sortedMap = new TreeMap<>(unsortedMap);             // 打印排序后的Map             for (Map.Entry<String, String> entry : sortedMap.entrySet()) {                 System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());             }             //3.使用&和=拼接             StringBuilder sb = new StringBuilder();             for (Map.Entry<String, String> entry : sortedMap.entrySet()) {                 if (sb.length() > 0) {                     sb.append("&"); // 在第一个键值对之后添加"&"                 }                 sb.append(entry.getKey()).append("=").append(entry.getValue());             }             String result = sb.toString();             System.out.println(result); // 输出: key1=value1&key2=value2&key3=value3             //4.算MD5哈希             String javaMd5Hash = getMD5Hash(result);             System.out.println("JAVA算出的MD5值:" + javaMd5Hash  );          }catch (Exception ex){             System.out.println( "ex!"+ex.getMessage() );         }         System.out.println( "结束!" );     }      public static String getMD5Hash(String input) {         try {             byte[] byInput=input.getBytes();//默认是UTF-8             // 获取MD5 MessageDigest实例             MessageDigest md = MessageDigest.getInstance("MD5");             // 更新要计算哈希的输入             md.update(byInput);             // 完成哈希计算并返回             byte[] digest = md.digest();             // 将字节数组转换为16进制的字符串             StringBuilder sb = new StringBuilder();             for (byte b : digest) {                 sb.append(String.format("%02x", b & 0xff));             }             // 转换为小写(如果需要)             return sb.toString().toLowerCase();         } catch (NoSuchAlgorithmException e) {             throw new RuntimeException("MD5 not supported", e);         }     }

JAVA 效果:

Key = Dip4, Value = 10086 Key = Eip2, Value = 10010 Key = aip3, Value = 10000 Dip4=10086&Eip2=10010&aip3=10000 JAVA算出的MD5值:35dffab906c15e2f9d7d1f60c8817584

 

 

--END