【问题标题】:Java MD5 encoder not match with C# MD5CryptoServiceProviderJava MD5 编码器与 C# MD5CryptoServiceProvider 不匹配
【发布时间】:2015-01-29 13:50:22
【问题描述】:

我正在尝试生成C# MD5CryptoServiceProvider 来编码字符串和Java MessageDigest.getInstance("MD5") 来编码字符串,但是两个输出是不同的。 stackoverlflow 上已经有很多示例可用,但仍然停留在某些地方。

以下是我的 C# 代码:

MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
    Byte[] hashedDataBytes = null;
    UTF8Encoding encoder = new UTF8Encoding();

    hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes("NSI#1234@"));
    string strPassword = string.Empty;
    foreach (byte b in hashedDataBytes)
    {
        strPassword = strPassword + b.ToString();
    }
    return strPassword;

C# 代码是冻结代码,我无权更改此代码。

以下是我的 Java 代码:

MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] digest = messageDigest.digest("NSI#1234@".getBytes("UTF-8"));
String hash = new BigInteger(1, digest).toString();
System.out.println(hash);

C#代码输出:158163028351382321031971922721528189209213

Java 代码输出:210864369951346339831795420458152481237

C# 生成 42 个数字,java 生成 39 个数字。如果我更改 new BigInteger(1, digest).toString(8) 的值,它会生成 43 个数字,如果我更改 new BigInteger(1, digest).toString(9) 它会生成 41 个数字。

【问题讨论】:

  • 您的 C# foreach 将字节值连接为字符串并不等同于您在 Java 中使用 BigInteger 构造一个数字,然后再将其转为字符串。
  • @MarkRotteveel for java 我们需要做什么?
  • 你的 C# 代码没有你的 java 代码有意义(尽管通常你会直接将字节数组输出为两种语言的十六进制字符串)。
  • 如果您有诸如“我无法更改 C# 代码”之类的要求,请在问题中指定这些要求以开始。

标签: java c# md5 encode


【解决方案1】:

您的 C# 代码您的 Java 代码都不是将散列转换为字符串的好方法。我强烈怀疑您在这两种情况下都有相同的 bytes,但是您将它们转换为字符串的方式不同。

您的 C# 代码只是将每个字节转换为其 十进制 表示。 (通过重复的字符串连接也是如此。Ick。)您的 Java 代码将忽略前导 0,并且当前使用十进制。您可以调用toString(16) 生成十六进制,但它仍然会忽略前导零。

如果你确实想要十六进制,你可以使用BitConverter.ToString(byte[]) 在.NET 中获得一个十六进制表示,尽管你可能想要删除- 它将放置在每个字节之间;在 Java 中有各种可用的库,例如 Apache Commons Codec (Hex) 或 Guava (BaseEncoding.base16())。或者使用 许多 中的一些代码来回答 Stack Overflow 上 Java 中的十六进制编码,such as this one

或者,您可以使用 Base64 - 同样,有多个可用选项,例如 .NET 中的 BitConverter.ToBase64String 和 Java 中的 iharder public domain library

如果您的 C# 代码真的被冻结(跑到山上!),那么等效的(嗯,稍微干净一点)Java 代码将类似于:

StringBuilder builder = new StringBuilder();
for (byte b : digest) {
    builder.append(b & 0xff); // Convert signed to unsigned
}

如果您使用的是 Java 7+,我还建议您使用 StandardCharsets.UTF_8

但如果您可能可以,我会强烈建议修复 C# 代码。即使您处于“代码冻结”状态,这可能不会阻止您修复重要的错误 - 如果您要存储这些值,那么现在修复它会比现在更容易修复稍后会这样做。

【讨论】:

  • 如果你不想在 Java 中使用外部库,你也可以这样做:stackoverflow.com/a/13006907/466862
  • 感谢@Jon 现在我无法更改 c# 代码,这就像冻结代码,那么现在我该如何处理?
  • @HarmeetSinghTaara:那么您需要更改 Java 代码以执行相同的操作(我将添加一个示例) - 但您应该真的,真的更改 C# 代码只要你可能可以。当前的格式很可笑。
  • @HarmeetSinghTaara 如果你真的需要做同样的事情 - 真的错了 - C# 代码,那么从执行该代码的等效代码开始; Java 和 C# 之间的差异并没有那么大(除了字节在 C# 中是无符号的,而在 Java 中是有符号的)
  • @HarmeetSinghTaara C# 代码错误。它不计算 MD5 哈希。升级直到找到有线索的人并获得修复它的许可。或者更好:先修复它,稍后获得许可。
【解决方案2】:

正确的哈希

我已经在控制台上检查了正确的值:

$ export LC_ALL=en_US.UTF-8
$ export LANG=en_US.UTF-8
$ export LANGUAGE=en_US.UTF-8
$ echo -n "NSI#1234@" | md5sum.exe
9ea3001c238ae867c5c01bd71cbdd1d5 *-

所以您的 Java 代码的结果是正确的,只是显示不正确(见下文)。

Java 代码中的问题

您的 Java 代码没有问题,只是结果格式错误:它以十进制数而不是通常用于哈希的十六进制表示形式显示结果。

您应该在显示之前将数字转换为十六进制:

// [your code from above]

String hex=new BigInteger(1, digest).toString(16); // Hex, but without leading zeros
String fill=String.format("%0" + 32 + "d", 0) // This is ugly...
String hash=(fill+hex).substring(hex.length()) // ... and this is a hack to add leading zeros

C# 代码中的问题

您的 C# 代码连接了每个字节的十进制表示。虽然这适用于十六进制数字(只要您在每个步骤中添加前导零!),它根本不适用于十进制数字。所以C#代码是错误的,你必须修复它。

最好使用.NET提供的转换器,速度更快,工作正常:

// [your code from above]

string hex = BitConverter.ToString(hashedDataBytes).Replace("-", string.Empty);

【讨论】:

  • 请注意,“虽然这适用于十六进制”仅在您确保为每个字节提供两个字符时才有效。一种天真的“将每个字节转换为其十六进制表示”的方法很容易以同样的问题告终。
  • @JonSkeet 是的,你是对的!我会更新答案。
猜你喜欢
  • 2012-06-17
  • 1970-01-01
  • 2014-05-19
  • 2013-07-15
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多