Recent Entries
- OT - Google Maps: In...
- Email Hijacked?
- CFUnited: Refactorin...
- CFUnited: Continuous...
- CFUnited: Prototypin...
- CFUnited: All about ...
- CFUnited: Event Driv...
- CFUnited: Integrated...
- CFUnited: ColdBox Fr...
- CFUnited: Design Pri...
Popular Entries
- CFUnited: All about ...
- SAML and ColdFusion ...
- SAML and ColdFusion ...
- SAML and ColdFusion ...
- Import/Export in SQL...
- SAML and ColdFusion ...
- Improving Performanc...
- CFUnited: Google Web...
- Second Blog CFC Surv...
- SAML and ColdFusion ...
Top Commenters
- Nathan Mische (12)
- CFFusionDev (6)
- CFdevfusion (6)
- Peter Bell (4)
- Sean Corfield (3)
- Rey Bango (3)
- Terrence Ryan (3)
- ah7866 (3)
- Scott (2)
- Jim Priest (2)
Slideshows
Dresser/Changing Table...Images related to the lay...
Nursery renovations...
Pool Surprises...
Sponsored Links
Search
Subscribe
Enter your email address to subscribe to this blog.RSS
Tags
cfug cfunited coldfusion flex generalArchives
- Adobe (5) [RSS]
- AIR (6) [RSS]
- ASP.NET (2) [RSS]
- BlazeDS (1) [RSS]
- Books (1) [RSS]
- CFEclipse (5) [RSS]
- CFML (0) [RSS]
- CFUG (26) [RSS]
- CFUnited (23) [RSS]
- ColdFusion (53) [RSS]
- College Football (3) [RSS]
- Conferences (1) [RSS]
- Development Tools (3) [RSS]
- DIY (1) [RSS]
- Eagles (3) [RSS]
- Fireworks (1) [RSS]
- Flash (3) [RSS]
- Flex (10) [RSS]
- Flyers (2) [RSS]
- Frameworks (5) [RSS]
- General (28) [RSS]
- Hockey (2) [RSS]
- Hosting (1) [RSS]
- House (2) [RSS]
- HTML (2) [RSS]
- JavaScript (1) [RSS]
- Jobs (1) [RSS]
- Macromedia (0) [RSS]
- Misc. (5) [RSS]
- Model-Glue (4) [RSS]
- Navy Football (5) [RSS]
- onair2007Philadelphia (3) [RSS]
- onairbustour (3) [RSS]
- Open Source (0) [RSS]
- Other (2) [RSS]
- Other Sports (4) [RSS]
- Performance (3) [RSS]
- Personal (2) [RSS]
- Phillies (2) [RSS]
- Projects, User Group Manager (1) [RSS]
- Rant (1) [RSS]
- Rants (1) [RSS]
- SAML (6) [RSS]
- Site (1) [RSS]
- Soccer (4) [RSS]
- SQL Server (2) [RSS]
- Transportation (1) [RSS]
- Wedding (2) [RSS]
AGGREGATORS
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:
<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:
<!-- 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:
//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.


philduba.com




Comments
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.
The system has attempted to use an undefined value, which usually indicates a programming error, either in your code or some system code.
Null Pointers are another name for undefined values.
The error occurred in C:\Inetpub\wwwroot\Test\SamlResponse.cfm: line 15
13 :<cfset ksString = "C:\CFusionMX7\runtime\jre\bin\temp.keystore">
14 :
15 : <cfif objSamlTest.VerifySignature(token,ksString)>
Exceptions section
13:10:46.046 - java.lang.NullPointerException - in C:\Inetpub\wwwroot\Test\SamlResponse.cfm : line 15
So I get this error on line where objSamlTest.VerifySignature(token,ksString) is called. Any ideas?
FYI, before I added code from your comment section to VerifySignature method, I was getting this error
Cannot create an ElementProxy from a null argument
The error occurred in C:\Inetpub\wwwroot\Test\SamlResponse.cfm: line 15
13 : <cfset ksString = "C:\CFusionMX7\runtime\jre\bin\temp.keystore">
14 :
15 : <cfif objSamlTest.VerifySignature(token,ksString)>
Exceptions section
12:16:11.011 - org.apache.xml.security.exceptions.XMLSecurityException - in C:\Inetpub\wwwroot\Test\SamlResponse.cfm : line 13
Cannot create an ElementProxy from a null argument
Any ideas?
Also at one moment I got message in the cf code via cfthrow type ="saml"
The required form variable for SAML Authentication/Verification is not present.
It happened only once and after that no luck.
I dont know if CF takes time to refresh java class or something because how can error change from one thing to another in short period of time.
Just wanted to say that everything is working fine now. To be honest, I dont know how that <b>Null Pointers</b> error went away. I did try these steps though I am not sure if they had anything to do with solving the issue.
I deleted all temporary classes created by CF7 at this location C:\CFusionMX7\wwwroot\WEB-INF\cfclasses. Then restarted the CF Service. After some tries, I was getting same error but after a while everything changed and I got the desired result.
Thanks for the excellent tutorials you have on your site.
Nice article.
I am having a bit of trouble understanding the big picture. I hope you can help me-
From what I understand, there are 3 entities:
1. A source application, say, http://boing.com/HR
2. A destination application- http://boing.com/sales
3. Stuff required for SSO- http://boing.com/samlConsumer
- User is asked to login when he tries to go to http://boing.com/HR
- User browses the application.
- User is redirected to http://boing.com/sales during browsing.
- The Form kicks in and user is redirected to http://boing.com/samlConsumer
- /samlConsumer decodes the samlResponse.
And then what? How does the user get to his final destination (http://boing.com/sales)?
I am obviously missing something here...
Thanks.
ted
Here is my take on your scenario
when /samlConsumer decodes the samlResponse, there are 2 options
a. userid or anyinfo that is sent in samlResponse, is validated against database and if validation is successful, user is redirected to final destination i.e. http://boing.com/sales (http://boing.com/sales)
b. validation was unsuccessful and you throw the user to the login portion of the http://boing.com/sales (http://boing.com/sales) site or you do any action as agreed upon with the IDP. These are the things usually agreed upon by IDP and SP.
HTH, All the best. Let us know how it went.
I have a question and i believe you can help me. For Single sign on, there is a new case I have seen. The IDP is sending company id in AttributeValue element like this which is like your example in SAML post
<saml:Attribute Name="clientId">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">07825</saml:AttributeValue></saml:Attribute>
But they are sending the employee id encrypted in another element like this
<saml:EncryptedAttribute>
<xenc:EncryptedData Id="_67767" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:RetrievalMethod Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"...; URI="#_23232"/>
</ds:KeyInfo>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">something
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
<xenc:EncryptedKey Id="_idhere" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">cipher value here
</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference URI="#_uri here"/>
</xenc:ReferenceList>
</xenc:EncryptedKey>
</saml:EncryptedAttribute>xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/><xenc:CipherData" target="_blank">http://www.w3.org/2001/04/xmlenc#"/><xe... xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">certificate sent here</xenc:CipherValue></xenc:CipherData><xenc:ReferenceList><xenc:DataReference URI="#_uri here"/></xenc:ReferenceList></xenc:EncryptedKey>
</saml:EncryptedAttribute>
I have been asked to get employee id from this encryypted xml text.
Do you know or have a java program wherein the information can be decrypted and I can extract the employee id?
If you can suggest an algorithm, then that will be great also.
Thanks,
Thanks!