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);
我正在尝试生成 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);