哪个是适当的XML独占规范化?

时间:2010-02-04 15:46:29

标签: php xml canonicalization canonical-form xmlseclibs

我正在使用xmlseclibs来尝试签署一个SOAP文档,但它似乎不会以相同的方式规范事物,具体取决于我是签名还是验证。

我会举个例子。这是我试图签署的XML:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1" IssueInstant="2010-02-04T15:27:43Z" ResponseID="pfxe85313e6-e688-299a-df06-30f55e24f65a">
<samlp:Status>
<samlp:StatusCode Value="samlp:Requester"/>
</samlp:Status>
</samlp:Response>
</soapenv:Body>
</soapenv:Envelope>

我使用PHP编写了一些代码,使用公钥和私钥证书的组合对其进行签名,它似乎有效。它添加了<ds:Signature>元素以及所有适当的东西,它看起来很棒。但后来我通过在签名后立即尝试验证它来测试它,再次使用xmlseclibs(和公钥证书),但验证失败。所以完全相同的代码库正在进行签名和验证,但这两个过程由于某种原因不同意。

我向xmlseclibs添加了一些调试代码,以了解它正在做什么,我意识到它出现的签名密钥和它提出的验证密钥的原因是不同的,因为它在两种情况下以不同的方式规范化。当我告诉它签署<samlp:Response>元素时,这是它标记的规范形式(为了便于阅读,我在这里添加了新行):

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" IssueInstant="2010-02-04T15:27:43Z" MajorVersion="1" MinorVersion="1" ResponseID="pfxe85313e6-e688-299a-df06-30f55e24f65a" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<samlp:Status>
<samlp:StatusCode Value="samlp:Requester">
</samlp:StatusCode>
</samlp:Status>
</samlp:Response>

然而,当它验证签名时,这是它计算验证的规范形式(再次,我在这里添加了新行):

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" IssueInstant="2010-02-04T15:27:43Z" MajorVersion="1" MinorVersion="1" ResponseID="pfxe85313e6-e688-299a-df06-30f55e24f65a">
<samlp:Status>
<samlp:StatusCode Value="samlp:Requester">
</samlp:StatusCode>
</samlp:Status>
</samlp:Response>

正如您所看到的,此版本省略了xmlns:saml元素中的<samlp:Response>属性,而第一个则没有。 (请注意,这与xmlns:samlp属性不同,后者包含在两者中。)这看起来非常像xmlseclibs中的错误,但是如果我只是知道哪个规范,那么我很乐意修复自己形式是正确的。独占规范化是否应该省略该属性?还是应该包括在内?哪一个是正确的独家规范形式?

2 个答案:

答案 0 :(得分:3)

两者都不是正确的规范形式!

签名XML在非命名空间属性之后有一个名称空间声明,它违反了文档顺序规则:

  

命名空间节点的文档顺序位置比属性节点小。

验证XML完全缺少saml命名空间节点。 Canonicalisation不会仅仅因为没有引用它们的子内容而删除命名空间节点。它只删除冗余的命名空间节点(即已经在父节点上生效的命名空间)。

我对xmlseclibs的了解不足以说明为什么会发生这种情况,但这两方面都是错误的。 FWIW我的DOM上的c14n函数在这里说:

<samlp:Response xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" IssueInstant="2010-02-04T15:27:43Z" MajorVersion="1" MinorVersion="1" ResponseID="pfxe85313e6-e688-299a-df06-30f55e24f65a">
<samlp:Status>
<samlp:StatusCode Value="samlp:Requester"></samlp:StatusCode>
</samlp:Status>
</samlp:Response>

eta:我刚看了SVN中的xmlseclibs.php,并没有一个简单的解决办法,因为它目前的方法存在根本缺陷。它尝试创建一个“规范化的”DOM,然后使用普通的saveXML()将其序列化。由于存在关于saveXML不承诺遵循的属性顺序和字符转义的C14N序列化规则,因此无法实现这一点。

答案 1 :(得分:2)

您正在不正确地创建DOM文档并尝试使用无效的内存中树。在尝试签名之前,序列化并使用序列化结果或在树中正确创建名称空间声明。有关详细信息,请参阅错误报告:http://code.google.com/p/xmlseclibs/issues/detail?id=6