Различия в 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);

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

$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> будут разными. Таким образом, сервер является правильным, подпись недействительна, но я не могу понять, почему метод A работает, а B нет.

Author: neubert, 2013-08-10

2 answers

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

$merchantElement = $document->createElement('Merchant');
 0
Author: Musa, 2013-08-10 18:45:28

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

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

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

    return $document;
}

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

 0
Author: Jurian Sluiman, 2013-08-11 12:04:45