Hello!
I ran across a problem while verifying the signature of an SAML2 Assertion:
The Assertion is encrypted in the body of a message and after the successfull decryption i have to check its signature.
I use soap_wsse_verify_with_signature(), but it always signals an invalid digest.
As a result it is not possible to successful check the digest of such message.
Further investigations revealed that the incoming document defines a namespace on its toplevel element which is used later in a child node deeper in the tree.
Simplyfied example:
<s:a xmlns:s="urn:acme" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<s:b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">x</s:b>
</s:a>
The relevant namespace is xmlns:xs="http://www.w3.org/2001/XMLSchema".
During canonicalization, gSoap seems to shift the namespace declaration down to the node where the declaration is required:
<s:a xmlns:s="urn:acme">
<s:b xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">x</s:b>
</s:a>
This seems to be invalid as RFC3076 Canonical XML Version 1.0 - Chapter 4.6 Superfluous Namespace Declarations - states that
"... The root document element is handled specially since it has no parent
element. All namespace declarations in it are retained, except the
declaration of an empty default namespace is automatically omitted. ..."
The following program shows the effect.
Best regards
Soenke Schau
#include "stdafx.h"
#include "wst.nsmap"
#include "stdsoap2.cpp"
#include "dom.cpp"
#include <sstream>
int main()
{
std::string Content =
R"(
<s:a xmlns:s="urn:acme" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<s:b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">x</s:b>
</s:a>
)";
{
soap Context(SOAP_IO_DEFAULT | SOAP_DOM_TREE);
std::stringstream ContentStream;
ContentStream << Content;
Context.is = &ContentStream;
auto SamlAssertion = soap_new_xsd__anyType( &Context );
if( soap_begin_recv( &Context ) ||
soap_in_xsd__anyType( &Context, NULL, SamlAssertion, NULL ) == NULL ||
soap_end_recv( &Context )
)
{
std::cout << "Problem reading content\n";
return 12;
}
std::cout << "XML read - SOAP_DOM_TREE - content:\n";
std::cout << *SamlAssertion << std::endl;
}
{
soap Context(SOAP_IO_DEFAULT | SOAP_XML_CANONICAL | SOAP_DOM_ASIS );
std::stringstream ContentStream;
ContentStream << Content;
Context.is = &ContentStream;
auto SamlAssertion = soap_new_xsd__anyType( &Context );
if( soap_begin_recv( &Context ) ||
soap_in_xsd__anyType( &Context, NULL, SamlAssertion, NULL ) == NULL ||
soap_end_recv( &Context )
)
{
std::cout << "Problem reading content\n";
return 12;
}
std::cout << "XML read - SOAP_XML_CANONICAL | SOAP_DOM_ASIS - content:\n";
std::cout << *SamlAssertion << std::endl;
}
return 0;
}
Sorry, forgot to write that i'm using gSoap 2.8.42
The problem is not the RFC conflict but the inclusive/exclusive namespaces modes. Namespaces are required to be part of the signed XML, otherwise namespace bindings can be forged and become insecure. Why not use the code that is already available in the WSSE plugin? See the documentation here:
https://www.genivia.com/doc/wsse/html/wsse.html#wsse_6_4
Hello!
I should have written that i already use the available code in the WSSE plugin, and that my example from the original report is just a simplification for this case.
Here the excerpt:
This always fails in the soap_wsse_verify_SignedInfo() function with the following data (some data changed):
While debugging i found out, that the xs Namespace declaration at the root element is shifted to the "saml2:AttributeValue" Element (where the namespace is first used) when gSoap performs its own serialization in order to check the Message Digest.
Best regards
Sönke Schau
Problem solved! The 2.8.45 release will be available soon (ETA in a few days). The problem was a flag that was reset too early, leading to the PrefixList (with inclusive C14N prefixes) being ignored, resulting in the prefix binding being removed from the root. Added unit+regression tests for our QA (private repo).
Thank you very much!
Great project!
Great support!
Best regards!
Sönke