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

net,java,md5,v2024 · 浏览次数 : 0

小编点评

本文主要介绍了C#和Java中MD5哈希算法的使用及其互通性。文章首先概述了MD5算法的基本概念和应用场景,然后详细讲解了如何在C#和Java中实现MD5哈希计算,并对比了两种语言在处理字符串、加盐和签名时的差异。 1. **MD5算法概述**: - MD5是一种广泛使用的哈希算法,用于生成数据的唯一哈希值。 - 常用于密码存储、数字签名等安全领域。 2. **C#中MD5实现**: - 使用MD5Create方法创建MD5实例。 - 将字符串转换为byte数组,并使用ComputeHash方法计算哈希。 - 字符串转byte数组时,推荐使用UTF-8编码。 - 字节数组转字符串时,可选择16进制小写或BASE64字符串。 - 加盐可以防止MD5碰撞,提高密码安全性。 - 示例代码展示了如何计算MD5哈希值并将其转换为16进制小写和BASE64字符串。 3. **Java中MD5实现**: - 使用MessageDigest.getInstance方法获取MD5 MessageDigest实例。 - 更新输入字符串并计算哈希。 - 字符串转byte数组时,默认使用UTF-8编码。 - 字节数组转字符串时,可选择16进制小写或BASE64字符串。 - 示例代码展示了如何计算MD5哈希值并将其转换为16进制小写和BASE64字符串。 4. **C#与Java的互通性**: - 通过实际案例演示了C#和Java中MD5计算的互通性。 - 比较了C#和Java在处理字符串、加盐和签名时的差异。 - 结果显示,C#和Java计算出的MD5值相同,证明了两者在处理MD5算法时的互通性。 总的来说,本文详细介绍了C#和Java中MD5哈希算法的使用方法和互通性,为开发者提供了在实际应用中实现MD5哈希计算的参考。

正文

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进制小写。

 

 如果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
结束!

 

 

三、同一个字符串 .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
结束 。

所谓加盐,就是将原始字符串的头部或尾部加上一段固定的字符串,然后再去算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

 

与C#.NET与JAVA互通之MD5哈希V2024相似的内容:

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

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

C#.NET与JAVA互通之DES加密V2024

C#.NET与JAVA互通之DES加密V2024 配置视频: 环境: .NET Framework 4.6 控制台程序 JAVA这边:JDK8 (1.8) 控制台程序 注意点: 1.由于密钥、明文、密文的输入输出参数,都是byte数组(byte[]),所以:字符串转byte数组(byte[])环节,

C#.NET与JAVA互通之AES加密解密V2024

C#.NET与JAVA互通之AES加密解密V2024 视频: 注意点: 1. KEY 和 IV 从字符串转byte数组时,双方要约定好编码,一般是UTF8。 2.明文从字符串转byte数组时,双方要约定好编码,一般是UTF8,也可以GB2312,但不能Encoding.Default。 3.加密后的

.NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系

最近在不少自媒体上看到有关.NET与C#的资讯与评价,感觉大家对.NET与C#还是不太了解,尤其是对2016年6月发布的跨平台.NET Core 1.0,更是知之甚少。在考虑一番之后,还是决定写点东西总结一下,也回顾一下.NET的发展历史。 首先,你没看错,.NET是跨平台的,可以在Windows、

C#.Net筑基-基础知识

C# (读作C Sharp)是由微软公司开发的一种面向对象、类型安全、高效且简单的编程语言,最初于 2000 年发布,并随后成为 .NET 框架的一部分。所以学习C#语言的同时,也是需要同步学习.NET框架的,不过要要注意C#与.NET的对应版本。

Unity框架与.NET, Mono框架的关系

什么是C# C#是一种面向对象的编程语言。 什么是.NET .NET是一个开发框架,它遵循并采用CIL(Common Intermediate Language)和CLR(Common Language Runtime)两种约定, CIL标准为一种编译标准:将不同编程语言(C#, JS, VB等)使

像go 一样 打造.NET 单文件应用程序的编译器项目bflat 发布 7.0版本

现代.NET和C#在低级/系统程序以及与C/C++/Rust等互操作方面的能力完全令各位刮目相看了,有人用C#开发的64位操作系统: GitHub - nifanfa/MOOS: C# x64 operating system pro...,截图要介绍的是一个结合Roslyn和NativeAOT的实

.NET周报 【5月第4期 2023-05-27】

## 国内文章 ### C#使用词嵌入向量与向量数据库为大语言模型(LLM)赋能长期记忆实现私域问答机器人落地之openai接口平替 https://www.cnblogs.com/gmmy/p/17430613.html 在上一篇[文章](https://www.cnblogs.com/gmmy/

[转帖]别催了,别催了,这篇文章我一次性把 Shell 的内容说完

https://my.oschina.net/jiagoushi/blog/6037198 Shell 搜索与匹配 1、在文件中查找字符串 grep 命令可以搜索文件,查找指定的字符串。 $ grep myvar *.c 在这个例子中,我们搜索的文件全都位于当前目录下。因此,我们只使用了简单的 sh

如何洞察 .NET程序 非托管句柄泄露

## 一:背景 ### 1. 讲故事 很多朋友可能会有疑问,C# 是一门托管语言,怎么可能会有非托管句柄泄露呢? 其实一旦 C# 程序与 C++ 语言交互之后,往往就会被后者拖入非托管泥潭,让我们这些调试者被迫探究 `非托管领域问题`。 ## 二:非托管句柄泄露 ### 1. 测试案例 为了方便讲述