Различия в подписях XML между HEREDOC и DOMDocument

Клиент API, который я разработал, работает с сообщениями XML, и сообщения подписываются в соответствии с синтаксисом подписи XML и Обработка спецификации. После долгой борьбы я наконец заработал подписи.

На данный момент я создаю XML с помощью HEREDOC (просто строки php) и с очисткой, я хотел бы создать XML напрямую с DOMDocument. Однако это приводит к тому, что сообщение становится недействительным сервером.

Это текущая настройка (сервер принимает это сообщение после подписания):

$xml = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
  <createDateTimestamp>$timestamp</createDateTimestamp>
  <Merchant>
    <merchantID>$merchantId</merchantID>
    <subID>$subId</subID>
  </Merchant>
</DirectoryReq>
EOT;

$document = new DOMDocument();
$document->loadXML($xml);

Это объектно-ориентированный подход (сервер отклоняет это сообщение при подписании):

$document = new DOMDocument('1.0', 'UTF-8');
$request  = $document->createElement('DirectoryReq');

$xmlns = $document->createAttribute('xmlns');
$xmlns->value = 'http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1';

$version = $document->createAttribute('version');
$version->value = '3.3.1';

$request->appendChild($xmlns);
$request->appendChild($version);

$merchant = $document->createElement('Merchant');
$merchant->appendChild($document->createElement('merchantID', $merchantId));
$merchant->appendChild($document->createElement('subID', $subId));

$request->appendChild($document->createElement('createDateTimestamp', $timestamp));
$request->appendChild($merchant);

$document->appendChild($request);

В чем может быть разница, поскольку XML-подпись признается недействительной сервером? Код для подписи сообщения точно такой же. Сервер просто сообщает "Недействительная электронная подпись".

Если требуется, я могу показать больше кода.

РЕДАКТИРОВАНИЕ, дополнительные выходные данные и сравнение сгенерированного XML

Чтобы дать дополнительную информацию, это вывод первого (HEREDOC) xml, созданного с помощью $document->saveXml():

<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
  <createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
  <Merchant>
    <merchantID>0020XXXXXX</merchantID>
    <subID>0</subID>
  </Merchant>
</DirectoryReq>

Это вывод ($document->saveXML()) для второго метода (прямая генерация DOMDocument):

<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
  <createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
  <Merchant>
    <merchantID>0020XXXXXX</merchantID>
    <subID>0</subID>
  </Merchant>
</DirectoryReq>

В php var_dump() дает точно такую ​​же длину строки. Если я сравню обе строки (очевидно, ===), они будут одинаковыми. Если сравнить оба объекта, то они не одинаковы.

Пример подписи

Подписание происходит с библиотекой xmlseclibs с этим кодом (примечание. Оба типа подписываются одинаково! ):

public function sign(DOMDocument $document, $fingerprint, $keyfile, $passphrase = null)
{
    $dsig = new XMLSecurityDSig();
    $dsig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
    $dsig->addReference($document, XMLSecurityDSig::SHA256,
        array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'),
        array('force_uri' => true)
    );

    $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
    if ($passphrase !== null) {
        $key->passphrase = $passphrase;
    }

    $key->loadKey($keyfile, true);
    $dsig->sign($key);

    $dsig->addKeyInfoAndName($fingerprint);
    $dsig->appendSignature($document->documentElement);
}

Если я сброшу XML после его подписания, значения <DigestValue> и <SignatureValue> будут другими. Итак, сервер прав правильно, подпись недействительна, но я не могу понять, почему метод А работает, а Б — нет.


person Jurian Sluiman    schedule 10.08.2013    source источник
comment
более ранний ответ предполагает, что канонизация XML xmlseclibs ошибочна. Это также может объяснить ваш опыт. Тогда этот ответ, который вы даете, звучит просто вводит в заблуждение (в лучшем случае), если на этом конце есть ошибка в phpseclibs.   -  person hakre    schedule 24.07.2014


Ответы (2)


Вы перезаписываете $merchant при создании элемента Merchant, поэтому просто переименуйте переменную

$merchantElement = $document->createElement('Merchant');
person Musa    schedule 10.08.2013
comment
Это была опечатка, так как я получил данные из массива и скопировал/вставил их в чуть более читаемый формат. Все значения вводятся по мере необходимости, проблема в чем-то другом. - person Jurian Sluiman; 10.08.2013
comment
Спасибо за ответ, в любом случае :) - person Jurian Sluiman; 10.08.2013
comment
@JurianSluiman Без фактического кода вы не сможете получить большую помощь - person Musa; 10.08.2013
comment
вот примеры как для вывода XML, так и для функций подписи. Дайджест и сама подпись отличаются, что приводит к данной ошибке. - person Jurian Sluiman; 11.08.2013

Теперь я решил это, снова экспортируя и импортируя XML. Это довольно уродливо, но позволяет мне гибко обрабатывать DOMNodes.

protected function repairDOMDocument(DOMDocument $document)
{
    $xml = $document->saveXML();

    $document = new DOMDocument;
    $document->loadXML($xml);

    return $document;
}

Если есть предложение, как перестать это делать, буду рад это услышать.

person Jurian Sluiman    schedule 11.08.2013
comment
Нет необходимости заново изобретать велосипед, метод называется DOMDocument::normalizeDocument() и это не имеет никакого отношения к ремонту (кстати, перезагрузка была бы лучшим термином, поскольку это именно то, что вы делаете). - person hakre; 24.07.2014
comment
Прочитав ссылку, которую вы разместили в комментарии к моему вопросу, я мог бы исправить проблему, заменив $xmlns = $document->createAttribute('xmlns'); $xmlns->value = 'http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1'; на $document->setAttributeNS('http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1'). У меня нет времени разбираться в этом сейчас, но спасибо за этот ответ, и я проверю его! Если не получится, попробую normalizeDocument() как альтернативу. - person Jurian Sluiman; 24.07.2014