简体   繁体   中英

Unable to validate SAML assertion with ColdFusion

We are receiving a standard SAML 2.0 assertion from an Identify Provider, and I am unable to validate it using the only example ColdFusion 9 example code that exists on the internet.

The code below can be found on the internet as an example of how to validate SAML with ColdFusion. See this page: http://blog.tagworldwide.com/?p=19

The SAML XML is being sent to us as a form post. We've got a page set up to detect the incoming assertion. The relevant code is below:

xmlResponse = getHttpRequestData().content.Trim();
docElement = XmlParse(variables.xmlResponse);

Init = CreateObject("Java", "org.apache.xml.security.Init").Init().init();

SignatureConstants = CreateObject("Java", "org.apache.xml.security.utils.Constants");
SignatureSpecNS = SignatureConstants.SignatureSpecNS;
xmlSignatureClass = CreateObject("Java", "org.apache.xml.security.signature.XMLSignature");
xmlSignature = xmlSignatureClass.init(docElement.getElementsByTagNameNS(SignatureSpecNS,"Signature").item(0),"");

keyInfo = xmlSignature.getKeyInfo();
X509CertificateResolverCN = "org.apache.xml.security.keys.keyresolver.implementations.X509CertificateResolver";
keyResolver = CreateObject("Java", X509CertificateResolverCN).init();
keyInfo.registerInternalKeyResolver(keyResolver);
x509cert = keyInfo.getX509Certificate();

isValid = xmlSignature.checkSignatureValue(x509cert);

ColdFusion 9 doesn't have built in libraries to handle x509 validation, so two Java libraries were imported into our ColdFusion installation. These came from the Apache Santuario project. They are:

  • serializer-2.7.1.jar
  • xmlsec-1.5.3.jar

The code runs just fine, but it always outputs "NO", the signature is not valid. I am certain that the assertion should valid properly.

I admit that I am just cargo coding here as I am not familiar enough with Java to truly understand what is going on here.

I've tried everything I can think of. Can anyone offer any tips or ideas on what to check or what to modify to continue troubleshooting?

== UPDATE 1 - Added SAML Assertion Example ==

This sample assertion from Salesforce.com is nearly identical in format to the one we are receiving. Yes our assertion does include a public key in it, just as this one does (though truncated here).

<samlp:Response ID="_257f9d9e9fa14962c0803903a6ccad931245264310738" IssueInstant="2009-06-17T18:45:10.738Z" Version="2.0">
     <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
        https://www.salesforce.com
     </saml:Issuer>

     <samlp:Status>
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
     </samlp:Status>

     <saml:Assertion ID="_3c39bc0fe7b13769cab2f6f45eba801b1245264310738" 
        IssueInstant="2009-06-17T18:45:10.738Z" Version="2.0">
        <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
           https://www.salesforce.com
        </saml:Issuer>

        <saml:Signature>
           <saml:SignedInfo>
              <saml:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
              <saml:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
              <saml:Reference URI="#_3c39bc0fe7b13769cab2f6f45eba801b1245264310738">
                 <saml:Transforms>
                    <saml:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                    <saml:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                       <ec:InclusiveNamespaces PrefixList="ds saml xs"/>
                    </saml:Transform>
                 </saml:Transforms>
                 <saml:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                 <saml:DigestValue>vzR9Hfp8d16576tEDeq/zhpmLoo=
                 </saml:DigestValue>
              </saml:Reference>
           </saml:SignedInfo>
           <saml:SignatureValue>
              AzID5hhJeJlG2llUDvZswNUrlrPtR7S37QYH2W+Un1n8c6kTC
              Xr/lihEKPcA2PZt86eBntFBVDWTRlh/W3yUgGOqQBJMFOVbhK
              M/CbLHbBUVT5TcxIqvsNvIFdjIGNkf1W0SBqRKZOJ6tzxCcLo
              9dXqAyAUkqDpX5+AyltwrdCPNmncUM4dtRPjI05CL1rRaGeyX
              3kkqOL8p0vjm0fazU5tCAJLbYuYgU1LivPSahWNcpvRSlCI4e
              Pn2oiVDyrcc4et12inPMTc2lGIWWWWJyHOPSiXRSkEAIwQVjf
              Qm5cpli44Pv8FCrdGWpEE0yXsPBvDkM9jIzwCYGG2fKaLBag==
           </saml:SignatureValue>
           <saml:KeyInfo>
              <saml:X509Data>
                 <saml:X509Certificate>
                    MIIEATCCAumgAwIBAgIBBTANBgkqhkiG9w0BAQ0FADCBgzELM
                    [Certificate truncated for readability...]
                 </saml:X509Certificate>
              </saml:X509Data>
           </saml:KeyInfo>
        </saml:Signature>

        <saml:Subject>
           <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
              saml01@salesforce.com
           </saml:NameID>

           <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
           <saml:SubjectConfirmationData NotOnOrAfter="2009-06-17T18:50:10.738Z" 
              Recipient="https://login.www.salesforce.com"/>
           </saml:SubjectConfirmation>
        </saml:Subject>

        <saml:Conditions NotBefore="2009-06-17T18:45:10.738Z" 
           NotOnOrAfter="2009-06-17T18:50:10.738Z">

           <saml:AudienceRestriction>
              <saml:Audience>https://saml.salesforce.com</saml:Audience>
           </saml:AudienceRestriction>
        </saml:Conditions>

        <saml:AuthnStatement AuthnInstant="2009-06-17T18:45:10.738Z">
           <saml:AuthnContext>
              <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified
              </saml:AuthnContextClassRef>
           </saml:AuthnContext>
        </saml:AuthnStatement>

        <saml:AttributeStatement>

           <saml:Attribute Name="portal_id">
              <saml:AttributeValue xsi:type="xs:anyType">060D00000000SHZ
              </saml:AttributeValue>
           </saml:Attribute>

           <saml:Attribute Name="organization_id">
              <saml:AttributeValue xsi:type="xs:anyType">00DD0000000F7L5
              </saml:AttributeValue>
           </saml:Attribute>

           <saml:Attribute Name="ssostartpage" 
              NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">

              <saml:AttributeValue xsi:type="xs:anyType">
                 http://www.salesforce.com/security/saml/saml20-gen.jsp
              </saml:AttributeValue>
           </saml:Attribute>

           <saml:Attribute Name="logouturl" 
              NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">

              <saml:AttributeValue xsi:type="xs:string">
                 http://www.salesforce.com/security/del_auth/SsoLogoutPage.html
              </saml:AttributeValue>
           </saml:Attribute>
        </saml:AttributeStatement>
     </saml:Assertion>
  </samlp:Response>

== UPDATE 2 - Used keytool to add public key to java keystore ==

Added public key to java keystore file "cacerts" using keytool command. I can see that the public key has now been added to our cacerts file and that the cert is marked as "trusted".

We were sent their public key in a *.pem file, so I used that to add their key to the keystore. I also tried converting the *.pem file to a *.der file and importing that. Both worked just fine. However, my code still returns "NO" for isValid. Ugh.

The public key in the *.pem file exactly matches the public key that comes in the SAML assertion.

If it is not validating or you get "Cannot resolve element with ID." Add the third line below. What is happening is the newer version of Apache Santuario no longer assumes the IdAttribute is "ID". You need to manually set it.

xmlResponse = getHttpRequestData().content.Trim();
docElement = XmlParse(variables.xmlResponse);
docElement.setIdAttribute("ID",true); //Add this line

Since you mention Apache Santuario, here's a Java code sample from that project that involves validating an XML digital signature.

That sample uses the public key from the X509Certificate instead of the cert itself. Your code sample seems to be using the cert itself:

keyInfo = xmlSignature.getKeyInfo();
X509CertificateResolverCN = "org.apache.xml.security.keys.keyresolver.implementations.X509CertificateResolver";
keyResolver = CreateObject("Java", X509CertificateResolverCN).init();
keyInfo.registerInternalKeyResolver(keyResolver);
x509cert = keyInfo.getX509Certificate();

isValid = xmlSignature.checkSignatureValue(x509cert);

How about adjusting this to use the java.security.PublicKey from the X509Certificate :

keyInfo = xmlSignature.getKeyInfo();
X509CertificateResolverCN = "org.apache.xml.security.keys.keyresolver.implementations.X509CertificateResolver";
keyResolver = CreateObject("Java", X509CertificateResolverCN).init();
keyInfo.registerInternalKeyResolver(keyResolver);
x509cert = keyInfo.getX509Certificate();
publicKey = x509cert.getPublicKey()

isValid = xmlSignature.checkSignatureValue(publicKey);

Disclaimer: I've never written a line of CF in my life, so if it works but there are adjustments to make please comment and I'll adjust it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM