Java 和 C# 中使用 SHA-1 加密 ID 时,C# returns 值有时前面有一个额外的 0
When encrypting ID in Java and C# using SHA-1, C# returns value with an extra 0 in front sometimes
我有一个连接到另一个外部网站并传递 ID 号的网站,我正在处理并生成链接的页面是用 c# 编写的,而目标网站是用 Java 编写的,我我遇到了一些不匹配
我正在尝试在 C# 中使用与 Java 中的代码相同的方法来加密 ID,代码如下所示:
public static String shaHash(String input)
throws NoSuchAlgorithmException {
if (input == null) {
return null;
}
MessageDigest sha = MessageDigest.getInstance("SHA");
try {
sha.update(input.getBytes("UTF8"));
BigInteger hash = new BigInteger(1, sha.digest());
return hash.toString(16);
} catch (UnsupportedEncodingException e) {}
return null;
}
C# 代码看起来像
StringBuilder convertedId = new StringBuilder();
var idToByte = Encoding.ASCII.GetBytes(id);
using (SHA1 sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
for (int i = 0; i < bytes.Length; i++)
{
convertedId.Append(bytes[i].ToString("x2"));
}
}
return convertedId.ToString();
但是 return 值不同,因为 C# 代码似乎在开头添加了一个额外的 0:
Java:b83caf2f739209c42a8d02df0f6d4794f288703,以及
C#: 0b83caf2f739209c42a8d02df0f6d4794f288703
会是什么问题?我已尝试将 C# 代码中的编码更改为 UTF8,但仍然无效。
大家有什么想法吗?
非常感谢
您的转换循环不考虑第一个字节小于 16。请尝试类似:
StringBuilder convertedId = new StringBuilder();
var idToByte = Encoding.ASCII.GetBytes(id);
using (SHA1 sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
for (int i = 0; i < bytes.Length; i++)
{
if (i == 0 && bytes[i] < 16)
convertedId.Append(bytes[i].ToString("x1"));
else
convertedId.Append(bytes[i].ToString("x2"));
}
}
return convertedId.ToString();
编辑:考虑到 Peter O 在下面的回答,以下是更好的答案:
var idToByte = Encoding.ASCII.GetBytes(id);
using (var sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
return new BigInteger(bytes.Reverse().ToArray()).ToString("X");
}
不是那个:
1) 您需要添加对 System.Numerics
的引用
2) BigInteger 的构造函数期望数据以 Little Endian 顺序排列,因此是 Reverse 语句。
问题是,你要不要额外的0?
十六进制输出通常以每个字节 2 个十六进制数字的形式完成,因此您得到偶数位数,即前导 0 通常是您想要的。
Java 的 BigInteger
不会那样做,因为对它来说,输出是 base-16 number,而不是 [= 字节的 31=].
因此,与其在 C# 中删除多余的 0,不如在 Java 中添加前导 0。
您可以继续使用 BigInteger
,但如果结果字符串的长度为奇数(例如 s.length() % 2 != 0
),则在前面加上 0
,或者使用 hex-encoding 的其他方式byte[]
.
参见例如How to convert a byte array to a hex string in Java?
我开始这个之前给出的答案并不理想。请注意,Java 代码将哈希表示为 BigInteger
s,它们本质上是 数字 ,不一定是字节数组。 BigInteger
的 toString(16)
使用尽可能少的 base-16 数字打印出该数字,不带前导零。因此,如果该数字恰好小于 2152,则现有答案就有问题。匹配 Java 代码的更好解决方案是从 C# 代码中的 base-16 字符串中去除 all 前导零,而不仅仅是第一个(除非哈希码为 0).
我有一个连接到另一个外部网站并传递 ID 号的网站,我正在处理并生成链接的页面是用 c# 编写的,而目标网站是用 Java 编写的,我我遇到了一些不匹配
我正在尝试在 C# 中使用与 Java 中的代码相同的方法来加密 ID,代码如下所示:
public static String shaHash(String input)
throws NoSuchAlgorithmException {
if (input == null) {
return null;
}
MessageDigest sha = MessageDigest.getInstance("SHA");
try {
sha.update(input.getBytes("UTF8"));
BigInteger hash = new BigInteger(1, sha.digest());
return hash.toString(16);
} catch (UnsupportedEncodingException e) {}
return null;
}
C# 代码看起来像
StringBuilder convertedId = new StringBuilder();
var idToByte = Encoding.ASCII.GetBytes(id);
using (SHA1 sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
for (int i = 0; i < bytes.Length; i++)
{
convertedId.Append(bytes[i].ToString("x2"));
}
}
return convertedId.ToString();
但是 return 值不同,因为 C# 代码似乎在开头添加了一个额外的 0:
Java:b83caf2f739209c42a8d02df0f6d4794f288703,以及 C#: 0b83caf2f739209c42a8d02df0f6d4794f288703
会是什么问题?我已尝试将 C# 代码中的编码更改为 UTF8,但仍然无效。
大家有什么想法吗?
非常感谢
您的转换循环不考虑第一个字节小于 16。请尝试类似:
StringBuilder convertedId = new StringBuilder();
var idToByte = Encoding.ASCII.GetBytes(id);
using (SHA1 sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
for (int i = 0; i < bytes.Length; i++)
{
if (i == 0 && bytes[i] < 16)
convertedId.Append(bytes[i].ToString("x1"));
else
convertedId.Append(bytes[i].ToString("x2"));
}
}
return convertedId.ToString();
编辑:考虑到 Peter O 在下面的回答,以下是更好的答案:
var idToByte = Encoding.ASCII.GetBytes(id);
using (var sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(idToByte);
return new BigInteger(bytes.Reverse().ToArray()).ToString("X");
}
不是那个:
1) 您需要添加对 System.Numerics
的引用 2) BigInteger 的构造函数期望数据以 Little Endian 顺序排列,因此是 Reverse 语句。
问题是,你要不要额外的0?
十六进制输出通常以每个字节 2 个十六进制数字的形式完成,因此您得到偶数位数,即前导 0 通常是您想要的。
Java 的 BigInteger
不会那样做,因为对它来说,输出是 base-16 number,而不是 [= 字节的 31=].
因此,与其在 C# 中删除多余的 0,不如在 Java 中添加前导 0。
您可以继续使用 BigInteger
,但如果结果字符串的长度为奇数(例如 s.length() % 2 != 0
),则在前面加上 0
,或者使用 hex-encoding 的其他方式byte[]
.
参见例如How to convert a byte array to a hex string in Java?
我开始这个之前给出的答案并不理想。请注意,Java 代码将哈希表示为 BigInteger
s,它们本质上是 数字 ,不一定是字节数组。 BigInteger
的 toString(16)
使用尽可能少的 base-16 数字打印出该数字,不带前导零。因此,如果该数字恰好小于 2152,则现有答案就有问题。匹配 Java 代码的更好解决方案是从 C# 代码中的 base-16 字符串中去除 all 前导零,而不仅仅是第一个(除非哈希码为 0).