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
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>
Posted by Kerwin at 21:18 7 comments
Subscribe to:
Posts (Atom)