obaranovsky
obaranovsky

Reputation: 165

Proper XML Canonicalization for SAML

I am running into the problem with validating the signature for the SAML 2.0 Assertion XML. I am using the SAML2 library from simpleSAMLphp project, which in turn is using the PHP xmlseclibs library for signing XML and verifying the signatures.

I am receiving the following assertion from my partner:

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_c43265fe-8cd5-410f-b63d-dac9f266d4c9" IssueInstant="2015-01-23T17:46:28.456Z">
    <saml:Issuer>uat.test.com/saml2.0</saml:Issuer>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <Reference URI="#_c43265fe-8cd5-410f-b63d-dac9f266d4c9">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi" />
                    </Transform>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>mFKEIdw+cEielORqscbHuAJhI58=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>kEZHloxYJVqDg8oxLNpl+sbJYhv9r7yYU5yQi71gCNm/Cdtj9/P2LR5cnopKZZu+7j3PVimeZoir6RTTrdVKTLkp+PmvOmTlLH/LVtntQZ68TaUxUd3BvtQiKuJ8KFwWPmQ+W3RIKv4ySAsy6PUiWPcr8eIYpIiUA6rxCuSEpdA=</SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509Certificate>MIICczCCAdygAwIBAgIEdWfFczANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQIEwJNQTEQMA4GA1UEBxMHTm9yd29vZDEPMA0GA1UEChMGTWVyY2VyMRswGQYDVQQLExJNZXJjZXIgSFIgU2VydmljZXMxJTAjBgNVBAMTHE1lcmNlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNjI5MDI1NTQxWhcNMTcwNzI5MDQwMDAwWjB2MQswCQYDVQQIEwJNQTEQMA4GA1UEBxMHTm9yd29vZDEPMA0GA1UEChMGTWVyY2VyMRswGQYDVQQLExJNZXJjZXIgSFIgU2VydmljZXMxJzAlBgNVBAMTHk1lcmNlciBIdW1hbiBSZXNvdXJjZSBTZXJ2aWNlczCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAm9KNx7UmRbWwJ0gmIV9sZzMDH4uz+o6ZmOxVRLvrO0Yzc9Qsc+7f+jIEGsVpbeaj5zx+4c8g46QqGam/Ap+4peewduVAN7GwK8UrmveASQH1Y/byTVGhyndfEtHfauresYpNDbgCn/iq/cXNapuFaTB4U0MPtz8Bqds1871hNasCAwEAAaMQMA4wDAYDVR0PBAUDAwf/gDANBgkqhkiG9w0BAQUFAAOBgQBR5wV43k1XcIM7z24SBvpEDWgEawXZ3TjTI9/54v/ZAdzXAiYfLKVV+hiyum+QP9jezDUH+Wz2bqzjlxOSU7HvUn4MwN+88a1hvSXpPxsp7y4d7juVt66aip/9NuSWbNqUPdhgK/KvMHrZpksCoxUJG2+Q1Jz7aNiBQvONRRPdTg==</X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
    <saml:Subject>
        <saml:NameID>000786320</saml:NameID>
        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml:SubjectConfirmationData NotOnOrAfter="2015-01-23T17:51:28.471Z" Recipient="https://test.com/sso" />
        </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="2015-01-23T17:41:28.456Z" NotOnOrAfter="2015-01-23T17:51:28.456Z">
        <saml:AudienceRestriction>
            <saml:Audience>test.com:saml2.0</saml:Audience>
        </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AuthnStatement AuthnInstant="2015-01-23T17:46:28.456Z" SessionIndex="SI-8bd89651-62da-4b7d-9a54-04eb2eb90784">
        <saml:AuthnContext>
            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
        </saml:AuthnContext>
    </saml:AuthnStatement>
    <saml:AttributeStatement>
        <saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="email">
            <saml:AttributeValue>invalidemail@invalid.com</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="firstName">
            <saml:AttributeValue>Diane</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="lastname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="lastname">
            <saml:AttributeValue>Test</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="zipCode" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="zipCode">
            <saml:AttributeValue>02062</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="businessUnit" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="businessUnit">
            <saml:AttributeValue>78945</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="employeeID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="employeeID">
            <saml:AttributeValue>000786320</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
</saml:Assertion>

The signature element requests exclusive C14N canonicalization. The xmlseclibs library library canonicalizes this as following:

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_c43265fe-8cd5-410f-b63d-dac9f266d4c9" IssueInstant="2015-01-23T17:46:28.456Z" Version="2.0">
    <saml:Issuer>uat.test.com/saml2.0</saml:Issuer>
    <saml:Subject>
        <saml:NameID>000786320</saml:NameID>
        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml:SubjectConfirmationData NotOnOrAfter="2015-01-23T17:51:28.471Z" Recipient="https://test.com/sso"></saml:SubjectConfirmationData>
        </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="2015-01-23T17:41:28.456Z" NotOnOrAfter="2015-01-23T17:51:28.456Z">
        <saml:AudienceRestriction>
            <saml:Audience>test.com:saml2.0</saml:Audience>
        </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AuthnStatement AuthnInstant="2015-01-23T17:46:28.456Z" SessionIndex="SI-8bd89651-62da-4b7d-9a54-04eb2eb90784">
        <saml:AuthnContext>
            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
        </saml:AuthnContext>
    </saml:AuthnStatement>
    <saml:AttributeStatement>
        <saml:Attribute FriendlyName="email" Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>invalidemail@invalid.com</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="firstName" Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Diane</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="lastname" Name="lastname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Test</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="zipCode" Name="zipCode" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>02062</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="businessUnit" Name="businessUnit" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>78945</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="employeeID" Name="employeeID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>000786320</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
</saml:Assertion>

However the element my partner actually signs is this:

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_c43265fe-8cd5-410f-b63d-dac9f266d4c9" IssueInstant="2015-01-23T17:46:28.456Z" Version="2.0">
    <saml:Issuer>uat.test.com/saml2.0</saml:Issuer>
    <saml:Subject>
        <saml:NameID>000786320</saml:NameID>
        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml:SubjectConfirmationData NotOnOrAfter="2015-01-23T17:51:28.471Z" Recipient="https://test.com/sso"></saml:SubjectConfirmationData>
        </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="2015-01-23T17:41:28.456Z" NotOnOrAfter="2015-01-23T17:51:28.456Z">
        <saml:AudienceRestriction>
            <saml:Audience>test.com:saml2.0</saml:Audience>
        </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AuthnStatement AuthnInstant="2015-01-23T17:46:28.456Z" SessionIndex="SI-8bd89651-62da-4b7d-9a54-04eb2eb90784">
        <saml:AuthnContext>
            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
        </saml:AuthnContext>
    </saml:AuthnStatement>
    <saml:AttributeStatement>
        <saml:Attribute FriendlyName="email" Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>invalidemail@invalid.com</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="firstName" Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Diane</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="lastname" Name="lastname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Test</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="zipCode" Name="zipCode" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>02062</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="businessUnit" Name="businessUnit" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>78945</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="employeeID" Name="employeeID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>000786320</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
</saml:Assertion>

Pretty much the same, however without the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" namespace. The xsi namespace is mentioned in the signature Transform elements: <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi"/></Transform>

The SAML authentication fails because of this since the SHA1 digest in the signature does not match the digest xmlseclibs calculated on the element.

So I was wondering who is correct here -- does the xsi namespace have to be included (even though there are no elements in this namespace) because it is included in the InclusiveNamespaces, or if it is not, why does xmlseclibs includes that namespace there with C14N canonicalization?

Upvotes: 6

Views: 2689

Answers (1)

obaranovsky
obaranovsky

Reputation: 165

Actually some further debugging showed that the problem was with a bug in the SAML2 library (which is a part of smipleSAMLphp project: https://github.com/simplesamlphp/saml2).

When decrypting element SAML2 library does this:

    $xml = '<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '.
                 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' .
        $decrypted .
        '</root>';
    $newDoc = new DOMDocument();

(see https://github.com/simplesamlphp/saml2/blob/master/src/SAML2/Utils.php line 494)

This is a workaround in case there would be some orphan elements when only a subset of the document was serialized for encryption. Unfortunately this creates an extra xsi namespace, which shows up in the canonicalization down the road if xsi is included in the InclusiveNamespaces PrefixList.

Changing it to:

    $xml = '<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">'.
        $decrypted .
        '</root>';
    $newDoc = new DOMDocument();

actually does the trick in my case. However this is just a q quick hack and is not ideal since it may introduce some other errors for different implementations. I guess will need to work with the creators of the simpleSAMLphp library to find a more elegant solution for the long term.

Upvotes: 3

Related Questions