Friday, September 5, 2008

Single Sign-On with SAML and ColdFusion

My implementation of SAML is largely based on this post by David Rutter which is unfortunately riddled with errors that I spent more time than I would like to admit working through. His post and some of my background knowledge are from Phil Duba's Saml and ColdFusion Series which was very useful until about halfway through part 5 where it ventures out of my comfort zone into compiling java code for use within CF.

What I have done is merge the two approaches into a single solution usable on CF8(and possibly others although it has not been tested) for connecting to PingIdentity's PingFederate Service Provider server such as that used by Rearden Commerce.

PreReqs

  1. Download the binary(bin) Apache XML Security Library. I used the most current version 1.4.2.

  2. Unzip it and copy from xml-security-1_4_2\libs serializer.jar and xmlsec-1.4.2.jar into ColdFusion8\lib and restart the CF service

  3. Buy or Generate an x509 certificate and provide the public portion to you service provider. I will cover this in more depth in another post.


Now we are ready to get into the code.

We start with SAML Assertion XML and fill in the dynamic portions: ID's, dates and username.












#username#

urn:oasis:names:tc:SAML:1.0:cm:bearer









Now we will Sign our XML Assertion:


//injest the xml
samlAssertionElement = samlAssertionXML.getDocumentElement();
samlAssertionDocument = samlAssertionElement.GetOwnerDocument();
samlAssertion = samlAssertionDocument.getFirstChild();

//create the neccesary 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");

//set up the signature
sigType = XMLSignatureClass.ALGO_ID_SIGNATURE_RSA_SHA1;
signature = XMLSignatureClass.init(samlAssertionDocument, javacast("string",""), sigType);
samlAssertionElement.insertBefore(signature.getElement(),samlAssertion.getFirstChild());

//set up signature transforms
TransformsClass = CreateObject("Java","org.apache.xml.security.transforms.Transforms");
transformEnvStr = TransformsClass.TRANSFORM_ENVELOPED_SIGNATURE;
transformOmitCommentsStr = TransformsClass.TRANSFORM_C14N_EXCL_OMIT_COMMENTS;
transforms = TransformsClass.init(samlAssertionDocument);
transforms.addTransform(transformEnvStr);
transforms.addTransform(transformOmitCommentsStr);

KeyStoreClass = CreateObject("Java" , "java.security.KeyStore");
//injest your previously created keystore
ksfile = CreateObject("Java", "java.io.File").init("c:\temp.keystore");
inputStream = CreateObject("Java", "java.io.FileInputStream").init(ksfile);
ks = KeyStoreClass.getInstance("JKS");
ks.load(inputStream,"SamlTest");
keypw = "mypass";
key = ks.getKey("SamlTest",keypw.toCharArray());
cert = ks.getCertificate("SamlTest");
publickey = cert.getPublicKey();

signature.addDocument("", transforms);

//optionally include the cert and public key
//signature.addKeyInfo(variables.cert);
//signature.addKeyInfo(variables.publickey);

signature.sign(key);

samlAssertionXML = toBase64(toString(samlAssertionXML), "utf-8");



and then we use a form to post it to the service provider









And there you have homegrown SAML Single Sign-on Solution In ColdFusion.

Here is the source file in it's entirety because blogger has a tendency to mangle code

Please feel free to post questions or comments.

9 comments:

Phil D said...

Howard, good stuff. I'm in the middle of attempting SAML on CF8 and was trying to avoid using the libraries all together since the JRE that comes with CF8 supports the 1.3 version of the Apache XML Library. You state you are using the full libs from the 1.4.2 library, but my question is did you overwrite any of the existing CF libraries when you put the xml-security libraries in, specifically the xercesImpl.jar as this is a version greater than the one that ships with CF. Thanks!

Howard Ross said...

Phil,I'm still struggling to get my libraries set up correctly. I had SAML working but after restarting the box CF stopped working entirely. Ill keep you posted

Howard Ross said...

I once again have SAML working on CF8. I copied the contents of the xml-security-1_2_0\libs folder into the ColdFusion8\libs folder but did not overwrite any that were already existing.

Howard Ross said...

better yet, just copy from xml-security-1_4_2\libs serializer.jar and xmlsec-1.4.2.jar into ColdFusion8\libs and restart the CF service.

Phil D said...

Thanks for the info Howard. I'm glad to see only those two libraries are needed for CF8.

Howard Ross said...

adding attributes for on the fly user provisioning is causing my dev server to crash

Howard Ross said...

I setup another dev server, which shows the necessary jars in the class path yet i get an java object instantiation error

Unknown said...

Howard, great job but I have a problem.
If I want to put the tag inside the tag , how can I do?
Thanks!
Marco

Mark Kruger said...

Howard... can you contact me - I'd like some help with a SAML project and I'll make it worth your while. My name is Mark Kruger (Coldfusion Muse and CF Webtools). you can contact me through my blog, site, twitter, cftalk or my e-mail which is mkruger ... at cf webtools.