Generate an XML signature digest using PHP

流过昼夜 提交于 2020-01-23 12:12:19

问题


I'm trying to implement WS-Security for my PHP SOAP client. The first step is being able to generate a valid XML digest from the outgoing request, but I haven't been able to do this. I've been looking for answers for a few days now but most of the answers end up being something like "don't solve it yourself, just use an existing Java library". That's not feasible in my current situation.

I have been looking at several examples on the net trying to reproduce the same digest they have, for example this one from Microsoft. That page lists the following example:

<ds:Object Id="ts-text">
    Wed Jun  4 12:11:06 EDT
</ds:Object>

Then they show the expected digest value:

<ds:Reference URI="#ts-text">
    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <ds:DigestValue>pN3j2OeC0+/kCatpvy1dYfG1g68=</ds:DigestValue>
</ds:Reference>

This is the code I've been using to calculate the digest value:

<?php
$digest = base64_encode(hash('SHA1', $contents, true));

I've tried many different combinations of removing whitespace or using just the timestamp with no XML tags with no success. I've also tried more complex examples where cannonicalization is needed. This is one of my unit tests:

public function testCreateDigest(DOMDocument $request, $expectedDigest) {

    $ns = $request->documentElement->namespaceURI;
    $body = $request
            ->getElementsByTagNameNS($ns, 'Body')
            ->item(0);

    $firstElement = '';
    foreach($body->childNodes as $node){
        if ($node->nodeType === XML_ELEMENT_NODE) {
            $firstElement = $node;
            break;
        }
    }


    $content = $firstElement->C14N(false, true);


    $actualDigest = base64_encode(hash('SHA1', $content, true));

    $this->assertEquals($expectedDigest, $actualDigest);

}

What exactly am I supposed to hash? Am I missing any steps?


回答1:


I found the solution. For most of the examples I tried, the required transformation was exclusive canonicalization with comments: http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/#WithComments

So my problem was that I was doing the transformation wrong. The first parameter in PHP's C14N function defines whether to use exclusive transformation or not. This is what the code should have been (note that I removed the unnecessary traversal to the first element):

public function testCreateDigest(DOMDocument $request, $expectedDigest) {

    $ns = $request->documentElement->namespaceURI;
    $body = $request
        ->getElementsByTagNameNS($ns, 'Body')
        ->item(0);

    $content = $body->C14N(true, true); // <-- exclusive, with comments

    $actualDigest = base64_encode(hash('SHA1', $content, true));

    $this->assertEquals($expectedDigest, $actualDigest);

}

So there you have it. This served as a reminder for me to double check my XML and understand the standards before I go coding aimlessly.



来源:https://stackoverflow.com/questions/18857000/generate-an-xml-signature-digest-using-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!