Клиент 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>
будут другими. Итак, сервер прав правильно, подпись недействительна, но я не могу понять, почему метод А работает, а Б — нет.