phpxmlxmlseclibs

How can i add Signature to a specific place in XMLdocument?


I have XML document:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header/>
    <soapenv:Body>
        <saml2p:ArtifactResolve xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Version="2.0"
                                IssueInstant="2020-06-01T10:25:15+02:00">
            <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">TEST</saml2:Issuer>
            <saml2p:Artifact>AAQAAKFbFR94fxqmioAqjJUwfHTFVHTDBTVHdBwwTW+ehcM19zsk=</saml2p:Artifact>
        </saml2p:ArtifactResolve>
    </soapenv:Body>
</soapenv:Envelope>

I try to do this in this way:

$results = array();
$filename = 'cert.p12';
$password = 'certpass';
$priv_key = openssl_pkcs12_read(file_get_contents($filename), $results, $password);

$doc = new DOMDocument();
$doc->loadXML($xml);
$xp = new DOMXPath($doc);
$xp->registerNamespace('soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
$xp->registerNamespace('saml2p','urn:oasis:names:tc:SAML:2.0:protocol');
$xp->registerNamespace('saml2','urn:oasis:names:tc:SAML:2.0:assertion');
$xp->registerNamespace('ds',XMLSecurityDSig::XMLDSIGNS);

$artifactResolveNode = $xp->query('/*[local-name()=\'Envelope\']/*[local-name()=\'Body\']/*[local-name()=\'ArtifactResolve\']')->item(0);

if($artifactResolveNode){

    $objDSig = new XMLSecurityDSig();
    $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
    $objDSig->addReference(
        $artifactResolveNode,
        XMLSecurityDSig::SHA256,
        array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', 'http://www.w3.org/2001/10/xml-exc-c14n#'),
        array('force_uri' => true)
    );
    $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
    $objKey->loadKey($results['pkey'], FALSE);
    $objDSig->sign($objKey);
    $objDSig->add509Cert($results['cert']);
    $objDSig->appendSignature($doc->documentElement());
    echo $doc->saveXML();
}

Output with Singnature in wrong location is:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header/>
    <soapenv:Body>
    <saml2p:ArtifactResolve xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Version="2.0" IssueInstant="2020-06-01T10:25:15+02:00" Id="pfx97783ab1-0339-0f2e-b759-9e9c07e347b0">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">TEST</saml2:Issuer><saml2p:Artifact>AAQAAKFbFR94fxqmioAqjJUwfyUtjJbv0uEPB7ooopodBwwTW+ehcM19zsk=</saml2p:Artifact></saml2p:ArtifactResolve>
    </soapenv:Body>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

  <ds:SignedInfo>
(...)
</soapenv:Envelope>

I want to put Signature node between ISSUER and ARTIFACT. This location is very important to correct send SOAP Envelope. It is possible?


Solution

  • I found! I need add code below:

    $artifactNode = $xp->query('/*[local-name()=\'Envelope\']/*[local-name()=\'Body\']/*[local-name()=\'ArtifactResolve\']/*[local-name()=\'Artifact\']')->item(0);
    

    and append Signature like this:

        $objDSig->insertSignature($artifactResolveNode,$artifactNode);