【问题标题】:Java MD5 Hash Not Matching .NET HashJava MD5 哈希与 .NET 哈希不匹配
【发布时间】:2011-03-23 14:29:13
【问题描述】:

我有一个用 C# 编写的 web 服务来处理一些值的验证。在其中,我需要检查调用 Java 客户端中生成的 MD5 哈希。

Java 客户端以这种方式生成哈希

Charset utf8Charset = Charset.forName("UTF-8");

byte[] bytesOfPhrase = phrase.getBytes(utf8Charset);
MessageDigest md = MessageDigest.getInstance("MD5");

byte[] thedigest = md.digest(bytesOfPhrase);
this._AuthenticationToken = new String(thedigest, utf8Charset);

C# webservice 以这种方式生成它的 has:

private static string HashString(string toHash)
{
    MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();

    byte[] hashedBytes = md5Provider.ComputeHash(_StringEncoding.GetBytes(toHash));
    return Convert.ToBase64String(hashedBytes);
}

我在 Java 代码中尝试了几个字符集,但它们都没有生成与 Java 生成的字符串类似的字符串。在每次调用期间使用相同的硬编码值(这意味着我已经对参数进行了硬编码,因此哈希值应该匹配)仍然会产生一个奇怪的 Java 字符串。

C# 哈希值示例:

6wM7McddLBjofdFJ3rU6/g==

我会发布 Java 生成的字符串示例,但它有一些非常奇怪的字符,我认为我不能在此处粘贴。

我做错了什么?

【问题讨论】:

    标签: java hash md5


    【解决方案1】:

    这基本上是损坏的代码:

    // Badly broken
    byte[] thedigest = md.digest(bytesOfPhrase);
    this._AuthenticationToken = new String(thedigest, utf8Charset);
    

    永远不要尝试通过将任意二进制数据传递给 String 构造函数来对其进行编码。 始终使用 base64、十六进制或类似的东西。 Apache Commons Codec 有一个 Base64 编码器,或者这个 public domain version 有一个更令人愉快的 API。

    等效的 C# 将是:

    // Equally broken
    byte[] hashedBytes = md5Provider.ComputeHash(Encoding.UTF8.GetBytes(toHash));
    return Encoding.UTF8.GetString(hashedBytes);
    

    MD5 摘要生成的二进制数据实际上是有效的 UTF-8 字节序列的可能性有多大?

    另外两点需要注意:

    • 您可以在 .NET 中使用 MD5 类更简单地获取 MD5 哈希:

      byte[] hash;
      using (MD5 md5 = MD5.Create())
      {
          hash = md5.ComputeHash(bytes);
      }
      // Use hash
      

      注意使用using 语句在之后处理实例。我对此的主要偏好是它更容易记住、阅读和输入MD5 而不是MD5CryptoServiceProvider :)

    • 你还没有说清楚_StringEncoding是什么,但是代码真的应该只使用Encoding.UTF8来匹配Java。

    【讨论】:

    • 您还应该在 MD5 上调用 Dispose(或 using)。这有一些安全隐患,如果你使用*CryptoServiceProvider 实现,会释放一些非托管资源。
    • _StringEncoding 实际上是 UTF8。抱歉,我没有将它包含在示例的顶部。我需要在校对方面做得更好。快速提问,因为我有点困惑。在您的 C# 代码中,它似乎不再是 base64 字符串,因此它将与上面的 java 代码匹配,对吗?在那种情况下,我不需要在 Java 客户端中有一个 base64 字符串,再次正确吗?
    • @Mike G:我给出的唯一 C# 代码是明确的broken 代码,它是执行散列本身的位的替代品。如果您需要使用文本进行通信,base64 是在两个客户端中使用的方式。
    • @Jon 好吧,我认为这是一个很好的解决方案,但是当它在 Cognos 中运行时,我无法以某种方式引用该 Apache Commons Codec。不将值转换为 base64 有什么缺点吗?
    • @Mike:你还没有告诉我们你用这些哈希值在做什么。你需要它们作为字符串吗?如果是这样,您必须找到 some 安全转换,base64 或 hex 是显而易见的选择。您是否也尝试过我链接到的公共域代码?
    【解决方案2】:

    您的 C# 摘要是 Base64 格式,但您的 Java 摘要不是。将thedigest 也转换为Base64。

    【讨论】:

    • 您能再深入一点解释一下吗?我正在尝试执行以下操作: this._AuthenticationToken = new sun.misc.BASE64Encoder().encode(thedigest);但我收到一个错误:访问限制:由于对所需库 C:\Program Files\Java\jre6\lib\rt.jar 的限制,无法访问 CharacterEncoder 类型的方法 encode(byte[]) 我还在吗缺少什么?
    • @Mike G:是的,你不应该使用 sun.* 类。有关 Java 中 base64 编码的其他两个选项,请参阅我的答案中的链接。
    【解决方案3】:

    在 C# 中,您使用 Base64 对字节进行编码。在 Java 中,您将字节解释为 UTF-8 字符串。

    【讨论】:

      【解决方案4】:

      您的 C# 代码将 MD5 哈希输出为 BASE64 编码,但 java 代码不会。 比较两个 MD5 哈希的通用方法是比较其十六进制表示(16 字节 -> 32 位)。

      【讨论】:

        猜你喜欢
        • 2014-05-19
        • 2013-08-20
        • 2020-12-08
        • 1970-01-01
        • 2011-09-12
        • 2019-10-10
        • 2012-07-22
        • 1970-01-01
        相关资源
        最近更新 更多