Problems with XML digest value (for The Danish Health Data Authority)

93 Views Asked by At

I want to create some XML which has to contain a digest value, i.e., a BASE64 encoded hash value of some of the XML in canonical (C14N) form, but the receiver does not accept the value my code calculates nor the XML when done according to the (faulty) documentation (and the calculations match the expected value) - unless I make a dirty hack which doesn't make sense to me.

I fortunately have access to an existing system which allows me to compare what the receiver expects and there I could see that my calculation gave another result than the existing system calculated when my code created the same XML as the existing system but I realized (after reading the (faulty) documentation) that the XML were supposed to have some extra namespace attributes - and suddenly my code calculated the correct digest value!?!

I.e., I can calculate the correct digest value by creating the XML in two passes! One with the incorrect XML (as documented in the (faulty) documentation) and store the calculated value and then the second version where the code insert the calculated value from the first pass.

Does this ring a bell for anyone? Anyone working with the Danish Health Data Authority?

I have the following helper functions:

function createElement(\DOMDocument $xml, \DOMNode /*DOMDocument&DOMElement*/ $parent, string $name) : \DOMElement {
    $node = $xml->createElement($name) or die("<span class=\"fail\">createElement($name) failed</span>");
    return $parent->appendChild($node);
}

function createElementNS(\DOMDocument $xml, \DOMNode /*DOMDocument&DOMElement*/ $parent, string $ns, string $name) : \DOMElement {
    $node = $xml->createElementNS($ns, $name) or die("<span class=\"fail\">createElementNS(..., $ns, $name) failed</span>");
    return $parent->appendChild($node);
}

function createAttribute(\DOMDocument $xml, \DOMElement $parent, string $name, string $value) : \DOMAttr {
    $attribute = $xml->createAttribute($name) or die("<span class=\"fail\">createAttribute(..., $name, $value) failed</span>");
    $attribute->value = $value;
    return $parent->appendChild($attribute);
}

function createAttributeNS(\DOMDocument $xml, \DOMElement $parent, string $name, string $value) : \DOMAttr {
    $attribute = $xml->createAttribute($name) or die("<span class=\"fail\">createAttribute{NS}(..., $name, $value) failed</span>");
    $attribute->value = $value;
    return $parent->appendChild($attribute);
}

function createTextNode(\DOMDocument $xml, \DOMElement $parent, string $text) : \DOMText {
    $text_node = $xml->createTextNode($text) or die("<span class=\"fail\">createTextNode($text) failed</span>");
    return $parent->appendChild($text_node);
}

and excerpts of the code look like this

$hack_pass = 0;

while (++$hack_pass <= 2) {
    $xml = new \DOMDocument('1.0', 'UTF-8');

    $xml_soapenv_envelope = createElementNS($xml, $xml, 'http://schemas.xmlsoap.org/soap/envelope/', 'soapenv:Envelope');
    createAttributeNS($xml, $xml_soapenv_envelope, 'xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
...
    createAttribute($xml, $xml_soapenv_envelope, 'id', 'Envelope');

    $xml_soapenv_header = createElement($xml, $xml_soapenv_envelope, 'soapenv:Header');
    $xml_wsse_security = createElement($xml, $xml_soapenv_header, 'wsse:Security');
    $xml_wsu_timestamp = createElement($xml, $xml_wsse_security, 'wsu:Timestamp');
    $xml_wsu_created = createElement($xml, $xml_wsu_timestamp, 'wsu:Created');
    createTextNode($xml, $xml_wsu_created, $created);

    if ($hack_pass == 1) {
        $xml_saml_assertion = createElementNS($xml, $xml_wsse_security, 'urn:oasis:names:tc:SAML:2.0:assertion', 'saml:Assertion');
    } else {
        $xml_saml_assertion = createElement($xml, $xml_wsse_security, 'saml:Assertion'); // Final XML
    }
...
    if ($hack_pass < 2) {
        $c14n = $xml_saml_assertion->C14N(TRUE);
        $DigestValue = base64_encode(hash('sha1', $c14n, TRUE));
    }
...
}
0

There are 0 best solutions below