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 代码将哈希表示为 BigIntegers,它们本质上是 数字 ,不一定是字节数组。 BigIntegertoString(16) 使用尽可能少的 base-16 数字打印出该数字,不带前导零。因此,如果该数字恰好小于 2152,则现有答案就有问题。匹配 Java 代码的更好解决方案是从 C# 代码中的 base-16 字符串中去除 all 前导零,而不仅仅是第一个(除非哈希码为 0).