Home Contact

PD Versus-inspired Logophilduba.com

Adventures in Web Application Develompent by Phil Duba

Recent Entries

Popular Entries

Top Commenters

  • Nathan Mische (11)
  • Peter Bell (4)
  • Terrence Ryan (3)
  • Scott (2)
  • Jim Priest (2)
  • David (2)
  • Scott Stroz (2)
  • Scott P (2)
  • Justin Alpino (2)
  • Ravneet (2)

Slideshows

Pool Surprises...

Sponsored Links

Text Link Ads

SAML and ColdFusion Part 6 : Validating an Assertion

Posted On May 10, 2007 7:32 AM By Phil in SAML,ColdFusion

Welcome to what I believe will be the last entry in my SAML and ColdFusion series. This entry concentrates on validating a SAML assertion which includes checking for conditions, validating the signature, and extracting the assertion attributes. This post builds off of all the previous posts, so if you are just coming to this series at this point in time, I would suggest reading each of the previous posts in this series to see the background information. In the last post, we created a way to sign a document and create a keystore with a self-signed certificate. In this entry, we'll validate the assertion that was created, creating both a ColdFusion template for validation and a Java class file for verification.

At the end of the last post, the string returned from the Java class file included the digital signature. According to the SAML specification, when using the HTTP Binding, the assertion is to be submitted as a form variable using a Base64 encoding. The code for this would be rather simple:

<form method="post" action="https://thirdparty.services.com/samlConsumer" name="samlSSO">
<cfoutput>
<input type="hidden" name="SAMLResponse" value="#ToBase64(token,"utf-8")#"/>
</cfoutput>
</form>
</script language="JavaScript">
function submitSSO(){
document.samlSSO.submit();
}
setTimeout("submitSSO()",100);
</script>

Now, decoding and validating the SAML Assertion is relatively straightforward, from a ColdFusion perspective:

<cfif StructKeyExists(form,"samlResponse") AND Len(form.samlResponse) GT 0>
<!-- This assumes ColdFusion MX 7 -->
<cfset token = CharsetEncode(BinaryDecode(form.samlResponse,"Base64"),"utf-8")>
<cfset objSamlTest = CreateObject("java","path.to.SamlTest").init() />
<cfset ksString = "#GetTempDirectory()#temp.keystore">
<cfif objSamlTest.VerifySignature(token,ksString)>
   <!--
Peform the actions for other validations such as checking the NotAfter or NotBefore conditions
as well as any items you need to retrieve from the Attributes area.
-->

<cfset conditions = XmlSearch(tokenXml,"//*[namespace-uri() != '' and local-name()='Conditions']")> <cfif ArrayLen(conditions) EQ 0>
<cfthrow type="saml" message="No Conditions exist for the submitted SAML Assertion.">
</cfif>
<!-- This part will vary depending on the conditions expected to receive -->
<!--
DateConvertISO8601 can be found at http://www.cflib.org and was written
by David Satz (david_satz@hyperion.com). It can be downloaded at
http://www.cflib.org.
-->

<cfset condDate = DateConvertISO8601(conditions[1].XmlAttributes.Before, 0)>
<cfif DateCompare(DateConvert("local2Utc",now()),condDate) LTE 0>
   <cfthrow type="saml" message="NotBefore condition is invalid. Assertion validation failed.">
</cfif>

<cfset condDate = DateConvertISO8601(conditions[1].XmlAttributes.NotAfter, 0)>
<cfif DateCompare(DateConvert("local2Utc",now()),condDate) GTE 0>
   <cfthrow type="saml" message="NotAfter condition is invalid. Assertion validation failed.">
</cfif>

<cfelse>
<cfthrow type="saml" message="The SAML Assertion cannot be verified.">
</cfif>
<cfelse>
<cfthrow type="saml" message="The required form variable for SAML Authentication/Verification is not present.">
</cfif>

So that logic wasn't as straightforward as one might have thought, however, a lot of it will depend on the conditions you are expecting and the attributes being passed. In fact, on the project I worked on, I created a library of validation functions just so I could keep all of that logic in the same place for future reuse. Building off of the previous Java class, the VerifySignature function will perform almost the reverse type of logic to verify the signature. It still creates an XML document, but this time, it extracts the signature, loads the keystore and then validates the signature. Please note, there are many different ways to perform the validation. I have used a straight keystore lookup method, but it could be much more complicated if you are looking at doing a generic solution, such as X509 certificate lookup based on passed information within the signature. The simple Java function to validate the signature would look like:

public boolean VerifySignature(String token, String certPath) throws Exception {
//Initialize the library org.apache.xml.security.Init.init();
   
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.newInstance();
dbf.setNamespaceAware(true);
dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(new org.apache.xml.security.utils.IgnoreAllErrorHandler());
      
byte inputBytes[] = token.getBytes();
Document doc = db.parse(new ByteArrayInputStream(inputBytes));
   
Element sigElement = null;
NodeList nodes = doc.getElementsByTagNameNS(org.apache.xml.security.utils.Constants.SignatureSpecNS,"Signature");
String password = "mypass";
   
if(nodes.getLength() !=0 ){
// Found Nodes for Signature element sigElement = (Element)nodes.item(0);
XMLSignature signature = new XMLSignature(sigElement,"");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(new File(certPath)),password.toCharArray());
PublicKey pubkey = ks.getCertificate("SamlTest").getPublicKey();
return signature.checkSignatureValue(pubkey);
}
return false;
}

Again, just like signing, the Document object is created, converting the passed string into an XML Document. After that, a search for the Signature node is performed. If the Signature node is not present, then it will return false, otherwise it steps through loading the keystore and then one method call is all that is needed to verify the signature.

Now, I based all of these entries on someone working with SAML 2.0. A friend of mine asked me to take a look at SAML being passed to his application and the above methods were failing. In his case, head had two Digital Signatures within his document and he was working in SAML 1.1. Staring with SAML 2.0, Assertion and the other major nodes in the SAML schema have the ID attribute. Prior to 2.0, the ID attribute was really AssertionID or something similar for the node in question. I found this blog entry on how to add the appropriate attribute to act as an ID, otherwise, one may get an error along the lines of "The Reference for URI ... has no XMLSignatureInput". The entry deals with signing, but you can use the same type of logic for verification as well.

This, most likely, concludes the series on working with SAML and ColdFusion. I know there is a lot of information out there on working with SAML, but some of it is overly complicated or applies only to specific libraries or vendor solutions. I hope the series has laid the ground work and can be a resource for others trying to do similar work with SAML and ColdFusion.

Related Blog Entries

Comments

Phil Duba's Gravatar I'm adding this comment into this blog post as the link in it is incorrect for the issue with AssertionID and SAML 1.1. In the VerifiySignature method, place the following code:

byte inputBytes[] = token.getBytes();
Document doc = db.parse( new ByteArrayInputStream(inputBytes));

// Set up required ID attribute
Element rootElement = doc.getDocumentElement ();
String uriRef = doc.getDocumentElement().getAttribute( "AssertionId");
Attr id = doc.getDocumentElement().getAttributeNode( "AssertionID" );
IdResolver.registerElementById(rootElement, id);

Element sigElement = null ;
NodeList nodes = doc.getElementsByTagNameNS(org.apache.xml.security.utils.Constants.SignatureSpecNS ,"Signature" );

Make sure you also import the right classes for the above code to work.
# By Phil Duba | 8/20/07 3:35 PM
Post Your Comments

Captcha

If you subscribe, any new posts to this thread will be sent to your email address.