C#.NET与JAVA互通之MD5哈希V2024
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的更多相关文章
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- java生成字符串md5函数类
import java.security.MessageDigest; /** * Md5 工具 */ public class Md5Util { private static MessageDig ...
- Java 自带MD5加密 Demo
package demo; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; pub ...
- Java 常见摘要算法——md5、sha1、sha256
目录 摘要算法简介 md5 使用jdk内置方法实现md5加密 使用bc方式实现md5加密 使用cc方式实现md5加密 sha1 使用jdk内置方法实现sha1加密 使用bc方式实现sha1加密 使用c ...
- Java 两次MD5
导入: import org.apache.commons.codec.digest.DigestUtils; 代码: public static String md5(String src) { r ...
- 全面解决.Net与Java互通时的RSA加解密问题,使用PEM格式的密钥文件
作者: zyl910 一.缘由 RSA是一种常用的非对称加密算法.所以有时需要在不用编程语言中分别使用RSA的加密.解密.例如用Java做后台服务端,用C#开发桌面的客户端软件时. 由于 .Net.J ...
- Java基础-使用JAVA代码剖析MD5算法实现过程
Java基础-使用JAVA代码剖析MD5算法实现过程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.
- java中使用MD5进行加密 BASE64Encoder 编码
原文地址:http://www.cnblogs.com/weiwangnuanyang/articles/4326336.html java中使用MD5进行加密 在各种应用系统的开发中,经常需 ...
- java中使用MD5进行加密
java中使用MD5进行加密 在各种应用系统的开发中,经常需要存储用户信息,很多地方都要存储用户密码,而将用户密码直接存储在服务器上显然是不安全的,本文简要介绍工作中常用的 MD5加密算法,希 ...
- Java 自带MD5 校验文件
http://www.iteye.com/topic/1127319 前天第一次发表博客到论坛,关于Java文件监控一文,帖子地址在:http://www.iteye.com/topic/112728 ...
随机推荐
- K8s 网关选型初判:Nginx 还是 Envoy?
简介: 本文将从性能和成本.可靠性.安全性 3 方面,对两大开源实现进行比对,希望对正在做 K8s 网关选型的企业有所借鉴. 作者:张添翼(澄潭) 为了避免混淆,我们先对一些关键定义做一些厘清: 传统 ...
- Fluid 0.5 版本发布:开启数据集缓存在线弹性扩缩容之路
简介: 为了解决大数据.AI 等数据密集型应用在云原生场景下,面临的异构数据源访问复杂.存算分离 I/O 速度慢.场景感知弱调度低效等痛点问题,南京大学PASALab.阿里巴巴.Alluxio 在 2 ...
- CNCF TOC 委员张磊:不断演进的云原生给我们带来了什么?
简介: 任何一种云原生技术,它不再是某种能力的弥补,而是更多地将云的能力以某种方式更简单.更高效地透出给我的应用去使用.无论是容器.K8s 还是 Service Mesh,他们都是在不同的环节帮助应用 ...
- [Py] Python 的 shape、reshape 含义与用法
shape 方法用于查看数据是几行几列的. reshape 方法用于不更改数据的情况下,重新把数据进行规划成指定的行数和列数. .reshape(-1, 1) -1 表示自动,1 表示整理成 1 列 ...
- 实验8 #第8章 Verilog有限状态机设计-2 #Verilog #Quartus #modelsim
2. 汽车尾灯控制器 2.1 实验要求:设计一个汽车尾灯控制电路. (1)功能:汽车左右两侧各有3个尾灯,要求控制尾灯按如下规则亮灭. 汽车沿直线行驶时,两侧指示灯全灭. 右转弯时,左侧的指示灯全灭, ...
- 基于 RedHat 系的 Linux 常用命令 & 常见系统设定
闲言碎语 除特定指明外,本文默认基于 RedHat Enterprise Linux 8+ 的阐述. 基于CentOS推出的开源系统,国内的阿里推出Anolis OS,华为的OpenEuler.为填补 ...
- 第三章-常用的渗透测试工具-(sqlmap)
常用渗透测试工具 1.sqlmap 支持的数据库:MySQL.Oracle.PostgreSQL.SQL Server.Access.IBM DB2.SQLite.Firebird.Sybase.SA ...
- 在英特尔至强 CPU 上使用 🤗 Optimum Intel 实现超快 SetFit 推理
在缺少标注数据场景,SetFit 是解决的建模问题的一个有前途的解决方案,其由 Hugging Face 与 Intel 实验室 以及 UKP Lab 合作共同开发.作为一个高效的框架,SetFit ...
- 以对象的方式访问html中的标签,比正则表达式更好用的方式获取html中的内容,linq方式直接获取所有的链接,更加先进的c#版本爬虫开源库
这是我本人自己写的一个开源库,现已经发布到nuget,可以直接在vs的nuget包管理中搜索到,或者可以到nuget官网下载:https://www.nuget.org/packages/ZmjCon ...
- gin框架对接快递100 查询快递跟踪记录 Golang实现快递查询
参考ui效果: https://www.kuaidi100.com/?from=openv gin框架: 请求地址 http://localhost:8822/kd100/auto_com_num?n ...