robrichards / xmlseclibs

A PHP library for XML Security
BSD 3-Clause "New" or "Revised" License
388 stars 181 forks source link

XMLSecurityDSig calculates signature for itself instead of the target document #187

Open TimSparrow opened 5 years ago

TimSparrow commented 5 years ago

I am trying to sign an iDealPro DirectoryRequest xml as follows:

<?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>2019-01-02T13:11:00Z</createDateTimestamp>
    <Merchant>
        <merchantID>002073121</merchantID>
        <subID>0</subID>
    </Merchant>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
            <Reference>
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <DigestValue>rrWRmKsGS3Z2XRWlQ6ryVXm9U/8MCnph84i//OMbfWo=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>pz0m0knc50edipCDkVYWHBGxTbId29F7n0oCgtz8Rbt8K4CVZA89MSTO7dmSMATf/hCEqdqMGPAZbuOnvYB7u15HKklk9zuQqAkt4SXCAC77S5ILnYLb0XMTYmGyWxCg3Etd6/gyviRcJRabRF5VGoVODM1i4NJhvkEvh07wimbkc9DzBKKA2f5xYlT04rQ31c6xDkU8a39vctyhGcpmHWG2qpGpvffcZBh3ekVQNEEwVqCsuntcbPJA2Pentqeh92YRsdyy7++0mPa6Jh7WpcqxzzgA4ld6LSGA64XZK45bzVgfhnUv1hWlp4d9hlNUlnoMJauqNiLkrJc+oJlxTA==</SignatureValue>
        <KeyInfo>
            <KeyName>a6b22f987b1db408b4f7c9b6a1230744963d7188</KeyName>
        </KeyInfo>
    </Signature>
</DirectoryReq>

Syntactically, the signed xml is valid (conforms the schema indicated) and signature looks ok. However, it fails to validate both on the receiving end, and internally, using verify() call.

I extended the main class to enable debugging:

class XMLSecurityDSig extends \RobRichards\XMLSecLibs\XMLSecurityDSig
{
    use enableZendLogging;               // logging trait that allows formatted logging to file, consistent with the zf1 framework being used in the project
    const LOG_NAME = 'xmldsig.log';  // default log name

The extended class has changes in the sign() method to see what is about to be signed, as follows:

public function sign($objKey, $appendToNode = null)
    {
        // If we have a parent node append it now so C14N properly works
        if ($appendToNode != null) {$this->resetXPathObj();
            $this->appendSignature($appendToNode);
            $this->sigNode = $appendToNode->lastChild;
        }
        if ($xpath = $this->getXPathObj()) {
            $query = "./secdsig:SignedInfo";
            $nodeset = $xpath->query($query, $this->sigNode);
            if ($sInfo = $nodeset->item(0)) {
                $query = "./secdsig:SignatureMethod";
                $nodeset = $xpath->query($query, $sInfo);
                $sMethod = $nodeset->item(0);
                $sMethod->setAttribute('Algorithm', $objKey->type);
                $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
                $sigValue = base64_encode($this->signData($objKey, $data));
                $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
        $this->debug(sprintf('%s: signed data={%s} for node=%s, cMethod=%s, signature={%s}',
                    __METHOD__,
                    $data,
                    $sInfo->nodeName,
                    $this->canonicalMethod,
                    $sigValue
                ));
                if ($infoSibling = $sInfo->nextSibling) {
                    $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
                } else {
                    $this->sigNode->appendChild($sigValueNode);
                }
            }
        }

(note the debug call)

The outer code calling the XMLSecurityDSig is as follows: ...

const DSIG_REF_OPTIONS = ['overwrite' => false, 'force_uri' => true]; // I do not need the Id attribute in the root node, as it violates the schema // some irrelevant code skipped protected function sign() { if(null === $this->dsig) { $this->initDsig(); // initializes the dsig object with parameters } $objKey = $this->initDsigKey(); // load the private key and passphrase from the database $this->insertThumbnail(); // inserts the thumbprint of the x.509 certificate matching the private key - Signature/KeyInfo/KeyName node

    $this->dsig->addReference($this->dom, XMLSecurityDSig::SHA256, [static::XML_DSIG_ENVELOPED], null, static::DSIG_REF_OPTIONS);

// $this->debug(sprintf('About to sign: %s, owned by %s', $this->dsig->sigNode->tagName, $this->dsig->sigNode->ownerDocument->tagName)); $this->dsig->sign($objKey, $this->dom->documentElement); }



**Expected results:**
signed data outputs a canonicalized version of the original document, containing the <Merchant> block

**Actual results:**
signed data is the <Signature> block - it signs itself. 

Am I doing something wrong? Or is it a bug I just found?