Dragonsaber Website Design Banners Computer Art Print Design Kerwin"s Resume About Dragonsaber

April 24, 2010

April 10, 2010

April 7, 2010

ColdFusion / SAML (Part 3)

The Signature

This is where all of the pieces come together... the libraries & keystore from Part 1 and the XML from Part 2. Here's the script that applies all of the algorithms and transforms:


<cfscript>

// 1) Injest the XML

samlAssertionElement = samlAssertionXML.getDocumentElement();

samlAssertionDocument = samlAssertionElement.GetOwnerDocument();

samlAssertion = samlAssertionDocument.getFirstChild();



// 2) Create Java Objects

SignatureSpecNS = CreateObject("Java", "org.apache.xml.security.utils.Constants").SignatureSpecNS;

TransformsClass = CreateObject("Java","org.apache.xml.security.transforms.Transforms");

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

XMLSignatureClass = CreateObject("Java", "org.apache.xml.security.signature.XMLSignature"); // Access XML Signature CLASS

CanonicalizerClass = CreateObject("Java", "org.apache.xml.security.c14n.Canonicalizer"); // Access Canonicalizer CLASS



// 3) Signature

sigType = XMLSignatureClass.ALGO_ID_SIGNATURE_RSA_SHA1; // Define signature algorithm

canonType = CanonicalizerClass.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; // Define canonicalizer algorithm

signature = XMLSignatureClass.init(samlAssertionDocument, javacast("string",""), sigType, canonType); // Create signature and apply algorithms



// 4) Transforms

TransformsClass = CreateObject("Java","org.apache.xml.security.transforms.Transforms"); // Access Transforms CLASS

IncNSClass = CreateObject("Java","org.apache.xml.security.transforms.params.InclusiveNamespaces"); // Access Inclusive Namespaces CLASS

transformEnvStr = TransformsClass.TRANSFORM_ENVELOPED_SIGNATURE;

transformOmitCommentsStr = TransformsClass.TRANSFORM_C14N_EXCL_OMIT_COMMENTS;

transformIncNS = IncNSClass.init(samlAssertionDocument, "ds saml xs xsi");

transforms = TransformsClass.init(samlAssertionDocument); // Apply the base transform

transforms.addTransform(transformEnvStr); // Apply enveloped signature

transforms.addTransform(transformOmitCommentsStr); // Apply comment exclusion

transforms.item(1).getElement().appendChild(transformIncNS.getElement()); // Apply namespaces



// 5) Keystore

ksfile = CreateObject("Java", "java.io.File").init("#FULL_PATH_OF_KEYSTORE#");

inputStream = CreateObject("Java", "java.io.FileInputStream").init(ksfile);

KeyStoreClass = CreateObject("Java" , "java.security.KeyStore");

ks = KeyStoreClass.getInstance("JKS");

ks.load(inputStream,"#STOREPASS#");

keypw = "#STOREPASS#";

key = ks.getKey("#KEYSTORE_ALIAS#",keypw.toCharArray()); // Must be the alias. Using the keystore filename returns an error. This is the private key.

cert = ks.getCertificate("#KEYSTORE_ALIAS#"); // Must be the alias. Using the keystore filename returns an error.

publickey = cert.getPublicKey();


// 6) Putting it Together

signature.addDocument("#ssouser.uuid#", transforms);

signature.addKeyInfo(variables.cert); // adds certificate to the signature

signature.addKeyInfo(variables.publickey); // ads public key to signature

signature.sign(key);



samlAssertionElement.insertBefore(signature.getElement(),samlAssertion.getFirstChild()); // Takes samlAssertionElement and inserts the signature before the first node of saml:Assertion

</cfscript>



1) The first part of this script is for injesting the SAML XML code. Then you're going to traverse the DOM to the location where the signature will be inserted. In this case, it will be the first child node of . This was the part I mentioned in Part 2 that had a CF/Java bug. When I had the full XML as a single variable, no matter what method I used to traverse the DOM (combinations of getFirstChild, getLastChild, getNextSibling, etc.), I couldn't get it to recognize the 2nd level... which is where the signature needs to be inserted (as a child of ). So by splitting it into the 3 variables, the middle variable is only one level. At first, I thought it was just a limitation of my Java skills, but I also had a Java expert look at the code and he also couldn't get out of the first level.

2) This portion is accessing various classes from the XML Security Libraries that were installed in Part 1.

3) Same as #2, but accessing signature related algorithms.

4) Same as #2, but accessing various transforms to apply.

5) This part is accessing the various parts of the keystore also created in Part 1.

6) The last part is adding more unique data to the signature. The unique user ID is thrown in along with the certificate and public key.

If you understood all of those steps... or even if you didn't, this should give you a sense of why SAML is super secure! Now I'm going to clean things up a bit:


<cfset samlAssertionXML = "#Replace(samlAssertionXML, "<?xml version=""1.0"" encoding=""UTF-8""?>", "", "all")#">
<cfset samlAssertionXML = "#Replace(samlAssertionXML, " xmlns:ds=""http://www.w3.org/2000/09/xmldsig##""", "", "all")#">
<cfset samlAssertionXML = "#Replace(samlAssertionXML, "<ds:Signature>", "<ds:Signature xmlns:ds=""http://www.w3.org/2000/09/xmldsig##"">", "all")#">
<cfset samlAssertionXML = "#responseOpen##samlAssertionXML##responseClose#">


1) The first line is removing the XML declaration because I have it hardcoded into the first piece of the XML already.

2) The next line is removing the namespace declaration because somewhere along the lines, in one of the Java methods, the declaration is getting added to every tag. You may not need to do this step, but I just implemented it because I wanted to get the XML as close as possible to my SP's XML spec.

3) The 3rd line just adds the namespace declaration back to <DS:SIGNATURE> tag.

4) The last line combines the 3 variables back together so that "samlAsertionXML" contains the entire XML document.

To get this ready for transport, the next step is to convert the XML to Base64...


<cfset samlAssertionXML = "#toBase64(toString(samlAssertionXML), "utf-8")#">


And finally, this can be sent over to the SP with an auto-submit form...


<form action="#SPService#" method="post">

<input type="hidden" name="SAMLResponse" value="#samlAssertionXML#" />

<input type="submit" value="Send to SP>

</form>

 
Dragonsaber