2

我开发的一个 API 客户端使用 XML 消息,并且消息根据XML 签名语法和处理规范进行签名。经过长时间的努力,我终于让签名工作了。

目前,我正在使用 HEREDOC(简单的 php 字符串)构建 XML 并进行清理,我想直接使用 DOMDocument 创建 XML。但是,这会导致消息被服务器无效。

这是当前设置(服务器在签名时接受此消息):

$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 无效的线索。

4

2 回答 2

0

我现在通过再次导出和导入 XML 解决了这个问题。这很丑,但让我可以灵活地处理 DOMNode。

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

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

    return $document;
}

如果有人建议如何停止这样做,我很高兴听到。

于 2013-08-11T12:04:45.047 回答
0

$merchant您在创建元素时正在覆盖Merchant,所以只需重命名变量

$merchantElement = $document->createElement('Merchant');
于 2013-08-10T18:45:28.063 回答