这是相当讨厌的。建议的方法似乎暗示您需要将 XML 文档解析为 DOM 树之类的东西,找到 MD5 校验和并将其存储以供将来参考。然后,在重新序列化文档并计算其 MD5 哈希值之前,您将用 0 替换校验和。这一切听起来可行但可能很棘手。我看到的主要困难是您对文档的新序列化可能与原始序列化不同,并且(与 XML 无关)差异(例如在属性值周围使用单引号或双引号、添加换行符甚至不同的编码)导致哈希不同。如果你走这条路,你需要确保你的应用程序和用于创建文档的过程首先做出相同的选择。对于这类问题,规范的 XML 是标准解决方案 (http://www.w3.org/TR/xml-c14n)。
但是,我会做一些不同的事情。如果运气好的话,应该很容易编写一个正则表达式来定位文件中的 MD5 哈希并将其替换为 0。然后您可以使用它来获取哈希并在重新计算哈希之前在 XML 文件中将其替换为 0。这回避了解析、更改和重新序列化 XML 文档的所有可能问题。为了说明,我将假设哈希 '33d4046bea07e89134aecfcaf7e73015' 存在于 XML 文件中,如下所示:
<docRoot xmlns='some-irrelevant-uri>
<myData>Blar blar</myData>
<myExtraData number='1'/>
<docHash MD5='33d4046bea07e89134aecfcaf7e73015' />
<evenMoreOfMyData number='34'/>
</docRoot>
(我称之为 hash.xml),MD5 应该被 32 个零替换(所以散列是正确的)并使用 perl、md5 和 bash 在 shell 命令行上说明该过程。 (鉴于正则表达式和散列库的存在,希望将其翻译成 C 语言不会太难。)
分解问题,您首先需要能够找到文件中的哈希:
perl -p -e'if (m#<docHash.+MD5="([a-fA-F0-9]{32})#) {$_ = "$1\n"} else {$_ = ""}' hash.xml
(通过查找 docHash 元素的 MD5 属性的开头,允许其他可能的属性,然后抓取接下来的 32 个十六进制字符。如果找到它们,它会将它们插入魔术 $_ 变量中,如果不是将 $_ 设置为空,然后为每一行打印 $_ 的值。这会导致打印字符串“33d4046bea07e89134aecfcaf7e73015”。)
然后你需要计算文件的哈希值已经被零替换:
perl -p -e's#(<docHash.+MD5=)"([a-fA-F0-9]{32})#$1"000000000000000000000000000000#' hash.xml | md5
(其中正则表达式几乎相同,但这次十六进制字符被零替换并打印整个文件。然后通过 md5 散列程序将结果管道化来计算其 MD5。把这些放在一起有点 bash 给出:
if [ `perl -p -e'if (m#<docHash.+MD5="([a-fA-F0-9]{32})#) {$_ = "$1\n"} else {$_ = ""}' hash.xml` = `perl -p -e's#(<docHash.+MD5=)"([a-fA-F0-9]{32})#$1"000000000000000000000000000000#' hash.xml | md5` ] ; then echo OK; else echo ERROR; fi
执行这两个小命令,比较输出并在输出匹配时打印“OK”,如果不匹配则打印“ERROR”。显然这只是一个简单的原型,并且使用了错误的语言,我认为它说明了最直接的解决方案。
顺便说一句,为什么要将哈希值放在 XML 文档中?据我所见,与在侧通道上传递散列(甚至像在名为 documentname.md5 的第二个文件中那样简单)相比,它没有任何优势,并且使散列验证更加困难。