任何人都可以验证使用此方法获取md5哈希的正确性吗?

时间:2011-04-12 08:18:29

标签: java md5 hash

        MessageDigest m=MessageDigest.getInstance("MD5");
        StringBuffer sb = new StringBuffer();
        if(nodeName!=null) sb.append(nodeName);
        if(nodeParentName!=null) sb.append(nodeParentName);
        if(nodeParentFieldName!=null) sb.append(nodeParentFieldName);
        if(nodeRelationName!=null) sb.append(nodeRelationName);
        if(nodeViewName!=null) sb.append(nodeViewName);
        if(treeName!=null) sb.append(treeName);
        if(nodeValue!=null && nodeValue.trim().length()>0) sb.append(nodeValue);
        if(considerParentHash) sb.append(parentHash);
        m.update(sb.toString().getBytes("UTF-8"),0,sb.toString().length());
        BigInteger i = new BigInteger(1,m.digest());
        hash = String.format("%1$032X", i);

这些代码行背后的想法是我们将类/模型的所有值附加到StringBuilder中然后返回它的填充哈希值(Java实现返回长度为30或31的md5哈希值,所以最后一个line格式化要用0填充的哈希值。

我可以验证这是有效的,但我感觉它在某一点上失败了(我们的应用程序失败了,我相信这是可能的原因)。

任何人都可以看到为什么这不起作用的原因?是否有任何变通方法可以使此代码不易出错(例如,不需要将字符串设置为UTF-8)。

2 个答案:

答案 0 :(得分:1)

您的代码中有一些奇怪的东西。

字符的UTF-8编码可能使用多个字节。因此,您不应将 string length用作update()调用的最终参数,而是getBytes()实际返回的字节数组的长度。根据Paŭlo的建议,使用update()方法,该方法将byte[]作为参数。

MD5的输出是一个16字节的序列,具有完全任意的值。如果您将其解释为整数(这是您对BigInteger()的调用所做的那样),那么您将获得一个小于 2 160 ,可能小得多。转换回十六进制数字时,您可能会得到32,31,30 ......或少于30个字符。你对"%032X"格式字符串的使用留有足够的零,所以你的代码可以工作,但它是间接的(MD5的输出从来都不是一个整数)。

使用原始连接组装哈希输入元素。这可能会引发问题。例如,如果modeName为“foo”且modeParentName为“barqux”,则MD5输入将以({UTF-8编码)“{{开头1}}”。如果foobarqux为“modeName”且foobar为“modeParentName”,则MD5输入以“qux开头”。你没有说明为什么要使用散列函数,但通常,当使用散列函数时,它会对某些数据进行唯一的跟踪;两个不同的数据元素应该产生不同的哈希输入。

处理foobarqux时,调用nodeValue,这意味着此字符串可以以空格开头和/或结尾,并且您不希望将该空格包含在哈希输入中 - 但是执行包含它,因为您追加trim()而非nodeValue

如果您要执行的操作与安全性有任何关系,那么您不应该使用加密破坏的MD5。请改用SHA-256。

哈希XML元素通常通过规范化(处理空格,属性顺序,文本表示等)来完成。有关使用Java规范化XML数据的主题,请参阅this question

答案 1 :(得分:0)

这里可能存在一个问题:

m.update(sb.toString().getBytes("UTF-8"),0,sb.toString().length());

正如Robing Green所说,UTF-8编码可以产生一个byte[],它比原始字符串长(当字符串包含非ASCII字符时,它会完全执行此操作)。在这种情况下,您只是对String的开头进行哈希处理。

最好这样写:

m.update(sb.toString().getBytes("UTF-8"));

当然,如果您的字符串中包含非ASCII字符,这不会导致异常,只会产生另一个散列,否则会产生异常。你应该尝试将你的失败归结为SSCCE,就像lesmana推荐的那样。