Это довольно противно. Предлагаемый подход предполагает, что вам необходимо проанализировать XML-документ в нечто вроде дерева DOM, найти контрольную сумму MD5 и сохранить ее для дальнейшего использования. Затем вы должны заменить контрольную сумму на 0, прежде чем повторно сериализовать документ и вычислить его хэш MD5. Все это звучит выполнимо, но потенциально сложно. Основная сложность, которую я вижу, заключается в том, что ваша новая сериализация документа может не совпадать с оригинальной, и несущественные (для XML) различия, такие как использование одинарных или двойных кавычек вокруг значений атрибутов, добавленных разрывов строк или даже другой кодировки, заставить хэши отличаться. Если вы пойдете по этому пути, вам нужно будет убедиться, что ваше приложение и процедура, использованная для создания документа, в первую очередь, сделают одинаковый выбор. Для такого рода проблем канонический XML является стандартным решением (http://www.w3.org/TR/xml-c14n).
Однако я бы сделал что-то другое. Если вам повезет, будет довольно легко написать регулярное выражение, чтобы найти хеш MD5 в файле и заменить его на 0. Затем вы можете использовать это, чтобы получить хеш и заменить его на 0 в XML-файле перед повторным вычислением хеша. Это позволяет обойти все возможные проблемы с анализом, изменением и повторной сериализацией 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. (Надеюсь, что перевести это на C не составит труда, учитывая наличие библиотек регулярных выражений и хеширования.)
Чтобы устранить проблему, сначала нужно найти хеш, который находится в файле:
perl -p -e'if (m#<docHash.+MD5="([a-fA-F0-9]{32})#) {$_ = "$1\n"} else {$_ = ""}' hash.xml
(это работает, ища начало атрибута MD5 элемента docHash, учитывая возможные другие атрибуты, а затем захватывая следующие 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) и делает проверку хешей более сложной.