我正在尝试生成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个数字。
答案 0 :(得分:3)
您的C#代码和您的Java代码都不是将哈希转换为字符串的好方法。我强烈怀疑你在两种情况下都有相同的字节,但是你将它们转换为不同的字符串。
您的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#代码。即使你处于“代码冻结”状态,大概也不会禁止你修复重要的错误 - 如果你存储这些值,那么现在解决这个问题要容易得多。它会在以后这样做。
答案 1 :(得分: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代码没问题,它只是将结果格式错误:它将结果显示为十进制数,而不是通常用于哈希的十六进制表示。
您应该在显示之前将数字转换为十六进制:
// [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#代码是错误的,你必须修复它。
更好地使用.NET提供的转换器,它更快,并且可以正常工作:
// [your code from above]
string hex = BitConverter.ToString(hashedDataBytes).Replace("-", string.Empty);