Java MD5 编码器与 C# MD5CryptoServiceProvider 不匹配

Java MD5 encoder not match with C# MD5CryptoServiceProvider

我正在尝试生成 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# 代码 您的 Java 代码都不是将散列转换为字符串的好方法。我强烈怀疑你在这两种情况下都有相同的 bytes,但你将它们转换为不同的字符串。

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

如果你确实想要十六进制,你可以使用BitConverter.ToString(byte[]) to get a hex representation in .NET, although you may want to remove the - it will place between each byte; in Java there are various libraries available such as Apache Commons Codec (Hex) or Guava (BaseEncoding.base16()). Or use some code from one of the many answers to hex encoding in Java on Stack Overflow, 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# 代码。即使您在 "code freeze" 中,这可能不会禁止您修复重要的错误 - 如果您 存储 这些值,将更容易现在解决这个问题,以后再解决。

正确的散列

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

$ 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);