You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

针对丹麦卫生数据局的XML摘要值计算异常问题咨询

Issue with Valid XML Digest Generation for Danish Health Data Authority Integration

I'm currently stuck on a frustrating issue while generating an XML document (specifically a SOAP envelope with SAML assertions and WS-Security) that requires a digest value. The digest is supposed to be the Base64-encoded SHA1 hash of a portion of the XML after C14N canonicalization.

Here's the breakdown of what's happening:

  • Following the (incorrectly documented) specifications, neither my calculated digest nor the resulting XML are accepted by the receiving system (Danish Health Data Authority).
  • I can only get the setup to work with a hacky two-step process that makes no logical sense to me, and I'm hoping someone has encountered this before (especially folks who've worked with the Danish Health Data Authority).

The Bizarre Working Workflow

  1. First Pass: Generate an XML that includes an extra namespace attribute on the saml:Assertion element (as per the flawed documentation, which the final XML should NOT have). Calculate the digest from this element's C14N output and store it.
  2. Second Pass: Generate the final, "correct" XML (without that extra namespace on saml:Assertion), then insert the digest value calculated in the first pass.

When I generate XML matching the existing system's output directly, my digest doesn't match the existing system's. But when I follow this two-step hack, the digest is correct and the XML is accepted.

My 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); 
}

Relevant Code Snippet

$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#');
    // ... (other elements omitted for brevity)
    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) {
        // First pass: use namespace (incorrect for final XML) to generate valid digest
        $xml_saml_assertion = createElementNS($xml, $xml_wsse_security, 'urn:oasis:names:tc:SAML:2.0:assertion', 'saml:Assertion');
    } else {
        // Second pass: no namespace (correct final XML)
        $xml_saml_assertion = createElement($xml, $xml_wsse_security, 'saml:Assertion'); // Final XML
    }
    
    // ... (populate assertion content, same in both passes)
    
    if ($hack_pass < 2) {
        // Calculate digest from the first pass's assertion
        $c14n = $xml_saml_assertion->C14N(TRUE);
        $DigestValue = base64_encode(hash('sha1', $c14n, TRUE));
    }
    
    // ... (insert DigestValue into the Signature element in the second pass)
}

Has anyone dealt with a similar issue where the digest calculation depends on an "incorrect" version of the XML element? Or specifically worked with integrations for the Danish Health Data Authority that required this kind of workaround? I'd love to understand why this hack works and if there's a cleaner way to implement it.


内容的提问来源于stack exchange,提问作者Andrew Rump

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 22:17:34