【问题标题】:C# MD5 hash function return weird result?C# MD5 哈希函数返回奇怪的结果?
【发布时间】:2019-03-01 10:23:21
【问题描述】:

我刚刚尝试在 C# 中创建一个 MD5 哈希程序。我的朋友给了我一个示例代码,但是当我尝试使用“123456”运行测试时,而不是返回正确的哈希结果

e10adc3949ba59abbe56e057f20f883e

返回结果

ce0bfd15059b68d67688884d7a3d3e8c

我试图阅读主要代码,但仍然无法得到任何东西!

string value = textBox1.Text;

byte[] valueBytes = new byte[value.Length * 2];

Encoder encoder = Encoding.Unicode.GetEncoder();
encoder.GetBytes(value.ToCharArray(), 0, value.Length, valueBytes, 0, true);

MD5 md5 = new MD5CryptoServiceProvider();
byte[] hashBytes = md5.ComputeHash(valueBytes);

StringBuilder stringBuilder = new StringBuilder();

for (int i = 0; i < hashBytes.Length; i++)
{
    stringBuilder.Append(hashBytes[i].ToString("x2"));
}
textBox2.Text = stringBuilder.ToString();

【问题讨论】:

  • C# 的 MD5 没有什么奇怪的。散列算法返回字节,而不是字符串。如何将字节数组格式化为字符串取决于您。
  • Unicode 有些字符是两个字节,有些是一个字节。您假设所有字符都是两个字节。
  • @jdweng .NET 字符串是 UTF16 并且字符总是占用 2 个字节。一些代码点可能需要两个字符来表示。
  • @ABCD 什么原创?什么原始字节?散列适用于 bytes 而不是字符串。您使用 UTF16 编码将字符串转换为字节。如果你使用不同的,例如 ANSI,你会得到不同的字节。
  • “正确的哈希结果”:正如所说,哈希需要字节。关于“正确”是什么的缺失规范是使用“正确”字符编码,它将文本转换为字节。说正确结果是什么的人没有传达这一点。问。

标签: c# utf-8 md5


【解决方案1】:

您的朋友似乎使用了Encoding.Default 而不是Encoding.Unicode

.NET 中的字符串是 UTF16。不过,散列适用于 bytes,而不适用于字符串。字符串必须转换为字节。为此,必须使用特定的编码。

如果使用 .NET 原生编码,即 UTF16,原始字节缓冲区将是 12 个字节长,哈希的十六进制表示将是 ce0bfd15059b68d67688884d7a3d3e8c

var valueBytes=Encoding.Unicode.GetBytes("123456");
Debug.Assert(valueBytes.Length==12);
var md5=System.Security.Cryptography.MD5.Create();
byte[] hashBytes = md5.ComputeHash(valueBytes);
var hexText=String.Join("",hashBytes.Select(c=>c.ToString("x2")));

如果使用 7 位 US-ASCII 编码,则数组长度为 6 个字节,十六进制表示为 e10adc3949ba59abbe56e057f20f883e

var valueBytes=Encoding.ASCII.GetBytes("123456");
Debug.Assert(valueBytes.Length==6);

var md5=System.Security.Cryptography.MD5.Create();
byte[] hashBytes = md5.ComputeHash(valueBytes);
var hexText=String.Join("",hashBytes.Select(c=>c.ToString("x2")));

大多数代码页的前 127 个字节与 7 位 US-ASCII 字符匹配,因此 most encodings(包括 UTF8)将返回 e10adc3949ba59abbe56e057f20f883e。以下编码将返回相同的哈希字符串:Encoding.GetEncoding(1251)(西里尔文)、Encoding.GetEncoding(20000)(繁体中文)将产生相同的哈希。

Encoding.Default 值返回对应于计算机系统区域设置的编码。它是非 Unicode 应用程序使用的编码,例如使用 ANSI 字符串类型编译的 C++ 应用程序。

Encoding.GetEncoding(20273) 虽然会返回一个不同的值 - 这是一个 IBM EBCDIC,即使是英文字母和数字也使用不同的字节。这将返回:73e00d17ee63efb9ae91d274baae2459

【讨论】:

    【解决方案2】:

    您希望使用 UTF8 字符串,那么为什么要使用 Unicode 编码?使用 UTF8,你会得到你期望的结果:

    string value = "123456";
    
    byte[] valueBytes = new byte[value.Length]; // <-- don't multiply by 2!
    
    Encoder encoder = Encoding.UTF8.GetEncoder(); // <-- UTF8 here
    encoder.GetBytes(value.ToCharArray(), 0, value.Length, valueBytes, 0, true);
    
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] hashBytes = md5.ComputeHash(valueBytes);
    
    StringBuilder stringBuilder = new StringBuilder();
    
    for (int i = 0; i < hashBytes.Length; i++)
    {
        stringBuilder.Append(hashBytes[i].ToString("x2"));
    }
    
    Console.WriteLine(stringBuilder.ToString()); // "e10adc3949ba59abbe56e057f20f883e"
    

    【讨论】:

    • .NET 中的字符串是 UTF16。还没有 UTF8 字符串这样的东西。至于使用的编码,它是 US-ASCII,而不是 UTF8。 UTF8 的前 127 个字节在设计上与 US-ASCII 相同
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    相关资源
    最近更新 更多