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)。
答案 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推荐的那样。