robrichards/xmlseclibs

Set URI attribute with a fixed value

Closed this issue · 4 comments

sabas commented

In
https://github.com/robrichards/xmlseclibs/blob/master/src/XMLSecurityDSig.php#L662
I needed to change the line to read
$refNode->setAttribute("URI", '#'.$id_name)
Because I was trying to reproduce an XML which has

  <attachment id="attachmentId">[data]</attachment>

and below
<ds:Reference URI="#attachmentId">

Hello @robrichards!

I've the same question/issue.
I believe its already possible to sign a only a part of xml (with this libary) and it will get reflected in "Reference".

I'm looking for solution that will only sign a part of the xml file.

@sabas did you find a solution for this?

sabas commented

@BanalitoRaulito I was in a rush to complete the thing and I simply commented out the if block here
https://github.com/robrichards/xmlseclibs/blob/master/src/XMLSecurityDSig.php#L650

        /*
        if (! $node instanceof DOMDocument) {
            $uri = null;
            if (! $overwrite_id) {
                $uri = $prefix_ns ? $node->getAttributeNS($prefix_ns, $id_name) : $node->getAttribute($id_name);
            }
            if (empty($uri)) {
                $uri = self::generateGUID();
                $node->setAttributeNS($prefix_ns, $attname, $uri);
            }
            $refNode->setAttribute("URI", '#'.$uri);
        } elseif ($force_uri) {
            $refNode->setAttribute("URI", '#'.$uri);
        }
        */
        
        $refNode->setAttribute("URI", '#'.$id_name);

I hope it helps somehow :-)

        $doc = new \DOMDocument();
        $doc->loadXML('hole xml content');
        $element = $doc->getElementsByTagName('SignedProperties');

        $objDSig = new XMLSecurityDSig();

        $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
        
        $objDSig->addReference(
            $element[0],
            XMLSecurityDSig::SHA256,
            null,
            [
                'id_name' => 'id',
                'overwrite' => true,
            ]
        );
        
        $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']);
        $objKey->loadKey(base_path(...), true);
        $objDSig->sign($objKey);
        $objDSig->add509Cert(file_get_contents(...));
        $objDSig->appendSignature($doc->documentElement);

I managed to get it working!

The element in xml I wanted to sign is called "SignedProperties" and it has property id (note 'id_name' value in addReference).
addReference first value must be a DOMElement (not DOMDocument) to make use of this functionality.

Managed to review this after an upgrade broke again my patchwork :D
@BanalitoRaulito nice hint, that was the DOMElement, plus the id_name wasn't the one to be inserted in the document but the id of the existing element
In my case the xml to be signed contained this element

    $attachment = $xml->createElement('attachment', $attachment1);
    $attachment->setAttribute('id', 'attachmentId');
    $root->appendChild($attachment);

And the correct way to sign this is

    $objXMLSecDSig = new XmlSecurityDSig();
    $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);

    $objXMLSecDSig->addReference(
        $xml->getElementsByTagName('attachment')[0],
        XMLSecurityDSig::SHA256,
        [
            [
                'http://www.w3.org/TR/1999/REC-xpath-19991116' => [
                    'query' => "*[@id='attachmentId']",
                    'namespaces' => [
                        'ds' => 'http://www.w3.org/2000/09/xmldsig#',
                    ],
                ],
            ],
            'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
            'http://www.w3.org/2001/10/xml-exc-c14n#WithComments',
        ],
        [
            'overwrite' => false,
            'id_name' => 'id',
            'prefix_ns' => false
        ]
    );