Recent Entries
- Real Season Begins!
- Good User Interface ...
- Restarting the User ...
- Flex Builder 3 Requi...
- A Good Night!
- OT: What happened to...
- Using jQuery
- Philly CFUG Dec. 18 ...
- Next Philly CFUG
- Philly CFUG Tomorrow
Popular Entries
- Import/Export in SQL...
- SAML and ColdFusion ...
- SAML and ColdFusion ...
- SAML and ColdFusion ...
- SQL Server 2005 Expr...
- SAML and ColdFusion ...
- Improving Performanc...
- Learning Flex
- Second Blog CFC Surv...
- SAML and ColdFusion ...
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
Search
Subscribe
Enter your email address to subscribe to this blog.RSS
Tags
cfug coldfusion generalArchives
- Adobe (5) [RSS]
- AIR (6) [RSS]
- ASP.NET (2) [RSS]
- Books (1) [RSS]
- CFEclipse (5) [RSS]
- CFUG (24) [RSS]
- CFUnited (3) [RSS]
- ColdFusion (50) [RSS]
- College Football (3) [RSS]
- Conferences (1) [RSS]
- Development Tools (2) [RSS]
- DIY (1) [RSS]
- Eagles (3) [RSS]
- Fireworks (1) [RSS]
- Flash (3) [RSS]
- Flex (9) [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]
- 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 5 : Signing a Document
Posted On May 1, 2007 5:44 PM By Phil in SAML,ColdFusion
In this entry of the SAML series, I'll build a ColdFusion template as well as a Java class to sign an XML document. I'll also cover creating a keystore and self-signed certificate that will be used for the signature generation and verification. The logic created in this entry is heavily influenced by an article by Robert Sayre located here. So now, let's get on to the code!
Let's take the XML from part 2 of this series:
AssertionId="353FE620-1143-EBE0-E1C96F56FD89EE2E"
IssueInstant="2007-01-01 14:30:23Z"
Version="2.0">
<Issuer>https://www.identityprovider.com/IDP</Issuer>
<Conditions NotAfter="2007-01-01 14:31:23Z" NotBefore="2007-01-01 14:30:23Z">
<AudienceRestriction>
<Audience>http://www.serviceprovider.com/SP</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2007-01-01 14:30:23Z" SessionIndex="1234567890">
<AuthContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthContext>
</AuthnStatement>
<AttributeStatement>
<Subject>
<NameID format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">foppa@identityprovider.com</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" />
</Subject>
<Attribute AttributeName="idpSystemID" AttributeNamespace="http://www.identityprovider.com">
<AttributeValue>foppa21</AttributeValue>
</Attribute>
</Assertion>
Now, let's add some logic to the above XML to make sure we get something ColdFusion can generate.
<cfxml variable="samlAssert">
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
AssertionId="#CreateUUID()#"
IssueInstant="#DateFormat(DateConvert("local2utc", Now()),"YYYY-MM-DDT")##TimeFormat(DateConvert("local2utc",Now()),"HH:mm:SSZ")#"
Version="2.0">
<Issuer>https://www.identityprovider.com/IDP</Issuer>
<Conditions
NotAfter="#DateFormat(DateConvert("local2utc", Now()),"YYYY-MM-DDT")##TimeFormat(DateConvert("local2utc",DateAdd("n",1,Now())),"HH:mm:SSZ")#"
NotBefore="#DateFormat(DateConvert("local2utc", Now()),"YYYY-MM-DDT")##TimeFormat(DateConvert("local2utc",Now()),"HH:mm:SSZ")#"
>
<AudienceRestriction>
<Audience>http://www.serviceprovider.com/SP</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement
AuthnInstant="#DateFormat(DateConvert("local2utc", Now()),"YYYY-MM-DDT")##TimeFormat(DateConvert("local2utc",Now()),"HH:mm:SSZ")#"
SessionIndex="1234567890"
>
<AuthContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthContext>
</AuthnStatement>
<AttributeStatement>
<Subject>
<NameID format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">#qryUser.email#</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" />
</Subject>
<Attribute AttributeName="idpSystemID" AttributeNamespace="http://www.identityprovider.com">
<AttributeValue>#qryUser.spAccessId#</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
</cfxml>
</cfoutput>
Obviously, the specifics of the Assertion are going to depend on the implementation and logic surrounding the XML above. To build the Java class file is fairly straightforward and involves the following steps:
- Create a method that will accept a string input and a path to the certificate keystore (more on this later)
- Initialize the Apache XML Security Library
- Convert the submitted string of XML to an XML Document
- Create an XML Signature object based on this document and the certificate
- Add the XML Signature to the XML Document
- Convert the XML Document to a string to return to ColdFusion
The article referenced earlier has a prime example of the functionality to perform the above steps and my modifications of it follows:
import java.lang.*;
import java.lang.reflect.*;
import java.security.*;
import java.security.cert.*;
import java.security.interfaces.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerException;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.*;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xml.security.c14n.*;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.*;
import org.apache.xml.security.keys.*;
import org.apache.xml.security.keys.content.*;
import org.apache.xml.security.keys.content.x509.*;
import org.apache.xml.security.keys.keyresolver.*;
import org.apache.xml.security.keys.storage.*;
import org.apache.xml.security.keys.storage.implementations.*;
import org.apache.xml.security.utils.*;
import org.apache.xml.security.transforms.*;
import org.apache.xml.security.Init;
import org.apache.xml.serialize.*;
public class SamlTest {
/** Creates a new instance of SamlTest */
public SamlTest() { }
public String SignDocument(String token, String certPath) throws Exception {
org.apache.xml.security.Init.init(); // Initializes the Apache Security Library
String password = "mypass"; // Keystore Password
// Create Document builder factory DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.newInstance();
dbf.setNamespaceAware(true);
dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);
DocumentBuilder db = dbf.newDocumentBuilder(); // creates a new document builder db.setErrorHandler(new org.apache.xml.security.utils.IgnoreAllErrorHandler());
byte inputBytes[] = token.getBytes(); // converts inputted string into a byte array Document doc = db.parse(new ByteArrayInputStream(inputBytes)); // creates new XML Document
XMLSignature sig = new XMLSignature(doc, "", XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1); // Creates new signature Element rootElement = doc.getDocumentElement();
rootElement.insertBefore(sig.getElement(),rootElement.getFirstChild()); // Adds the signature as the first element of the document
Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
KeyStore ks = KeyStore.getInstance("JKS"); // Gets the Java Keystore ks.load(new FileInputStream(new File(certPath)),password.toCharArray()); // loads the certificate RSAPrivateKey privKey = (RSAPrivateKey) ks.getKey("SamlTest",password.toCharArray());
PublicKey pubKey = ks.getCertificate("SamlTest").getPublicKey();
sig.addDocument("",transforms,Constants.ALGO_ID_DIGEST_SHA1);
sig.sign(privKey); // signs the document using the private key
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
XMLUtils.outputDOM(doc, byteOut); // converts the XML document to a byte array return byteOut.toString();
}
}
Now, in order to load the certificate, we need a Java keystore file. One would create this file and self-signed certificate using the keytool of ColdFusion's JVM. An example of this creation is:
keytool -selfcert -alias SamlTest -genkey -keystore temp.keystore -keyalg RSA
The keytool will ask for passwords for both the keystore and the certificate. I used "mypass" for both, obviously, you'll want yours to be more secure. You will also be prompted for other information for the certificate. If you read my previous post, you will have seen that I state to compile the class in the location it will reside as called by ColdFusion. I am unsure why, but any time I moved the class file, I would get errors on invocation. As a result, I just compiled it into the directory I wanted it to reside. When compiling, the paths to the JAR files will have to be added either at the command prompt or the JAVACLASSPATH environment variable. Once the item is compiled, a signed document should be able to be created by executing the following code (continued from above):
<cfset ksString = "#GetTempDirectory()#temp.keystore">
<cfset token = objSamlTest.SignDocument(ToString(samlAssert),ksString) />
Finally, a before and after of the XML Signature is in the code snippet below (Note: These are formatted for readability, they are straight strings w/o formatting before and after signing).
<Assertion
AssertionId="49C68436-188B-10AF-6C4D7982EA429C75"
IssueInstant="2007-05-01T22:33:05Z"
Version="2.0"
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" >
<Issuer>https://www.identityprovider.com/IDP</Issuer>
<Conditions NotAfter="2007-05-01T22:34:05Z" NotBefore="2007-05-01T22:33:05Z">
<AudienceRestriction>
<Audience>http://www.serviceprovider.com/SP</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement
AuthnInstant="2007-05-01T22:33:05Z"
SessionIndex="1234567890">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
<AttributeStatement>
<Subject>
<NameID format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">test@someemail.com</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/>
</Subject>
<Attribute AttributeName="idpSystemID" AttributeNamespace="http://www.identityprovider.com">
<AttributeValue>someSharedUserName</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" AssertionId="49C68436-188B-10AF-6C4D7982EA429C75" IssueInstant="2007-05-01T22:33:05Z" Version="2.0"
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>xxdqAYZKpuYIYQkXY2vmMPzEAFQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue> nVBoMK4dFng/tJKqT7VPGPYg1CKs/5tK8TQXcsD3gDiGbQFDLNPchBNRtWs8DEY+L/EO+sl7Js3U z6/SReCGFJf2l23aNu5CBxl3eyJQPeF2viBKFRwm8P8itBvY5HT/a7wKzuPQd7SW2CyrK6NpbpYC PI9z+BqE9w7DxV16/3Q=
</ds:SignatureValue>
</ds:Signature>
<Issuer>https://www.identityprovider.com/IDP</Issuer>
<Conditions NotAfter="2007-05-01T22:34:05Z" NotBefore="2007-05-01T22:33:05Z">
<AudienceRestriction> <Audience>http://www.serviceprovider.com/SP</Audience> </AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2007-05-01T22:33:05Z" SessionIndex="1234567890">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
<AttributeStatement>
<Subject>
<NameID format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">test@someemail.com</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></SubjectConfirmation>
</Subject>
<Attribute AttributeName="idpSystemID" AttributeNamespace="http://www.identityprovider.com">
<AttributeValue>someSharedUserName</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
Well, that's all there is to signing the XML document. In the next entry of the series, we'll talk about validating the document.


philduba.com




Comments
Object Instantiation Exception. An exception occurred when instantiating a java object. The cause of this exception was that: .
The java code was compiled using same jars and jre library and the class file was not moved either.
Infact the error happens at org.apache.xml.security.Init.init(); // Initializes the Apache Security Library.
The code runs fine if I run it in Eclipse IDE as a standalone.
I really appreciate you help. Here are the details
Server Product ColdFusion MX
Version 7,0,2,142559
Edition Enterprise
----------------------------------------------------
JVM Details
----------------------------------------------------
Java Version 1.4.2_09
Java Vendor Sun Microsystems Inc.
Java Vendor URL http://java.sun.com/
Java Home C:\JRun4\jre
--------------------------------------------
Java Class Path
--------------------------------------------
...
C:\JCI\xml-security-1_2_0\libs\commons-logging-api.jar;
C:\JCI\xml-security-1_2_0\libs\commons-logging.jar;
C:\JCI\xml-security-1_2_0\libs\stylebook-1.0-b3_xalan-2.jar;
C:\JCI\xml-security-1_2_0\libs\xalan.jar;
C:\JCI\xml-security-1_2_0\libs\xercesImpl.jar;
C:\JCI\xml-security-1_2_0\libs\xml-apis.jar;
C:\JCI\xml-security-1_2_0\libs\xmlParserAPIs.jar;
C:\JCI\xml-security-1_2_0\libs\xmlsecSamples.jar;
C:\JCI\xml-security-1_2_0\libs\xmlsecTests.jar;
C:\JCI\xml-security-1_2_0\libs\xmlsec-1.2.96.jar;
C:\SingleSignOn\SamlTest\bin;
...
Same JRE library and jars were used to compile the code in Eclipse IDE. Again the class was not moved either. It runs fine as stand alone program in IDE. The issue occurs when invoking through ColdFusion.
This is the exception that I get when trying to initialize the Apache Security Library. It looks like log4j version problem, but I have followed the steps you have mentioned in Part 4.
08/30 01:33:17 error
[1]org.apache.commons.logging.LogConfigurationException: java.lang.ClassCastException
at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:558)
at org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:345)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:409)
at org.apache.xml.security.Init.<clinit>(Unknown Source)
at com.carcogroup.security.SamlTest.signDocument(SamlTest.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at coldfusion.runtime.StructBean.invoke(StructBean.java:391)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:1662)
at cftest2ecfm1084210002.runPage(C:\Inetpub\Onboarding\www\test.cfm:4)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:152)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:349)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:225)
at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:51)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:86)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:69)
at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:52)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:115)
at coldfusion.CfmServlet.service(CfmServlet.java:107)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:78)
at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:91)
at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:257)
at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:541)
at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:204)
at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:426)
at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
Caused by: java.lang.ClassCastException
at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:554)
... 33 more
[0]java.lang.ExceptionInInitializerError
at com.carcogroup.security.SamlTest.signDocument(SamlTest.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at coldfusion.runtime.StructBean.invoke(StructBean.java:391)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:1662)
at cftest2ecfm1084210002.runPage(C:\Inetpub\Onboarding\www\test.cfm:4)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:152)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:349)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:225)
at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:51)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:86)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:69)
at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:52)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:115)
at coldfusion.CfmServlet.service(CfmServlet.java:107)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:78)
at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:91)
at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:257)
at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:541)
at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:204)
at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:426)
at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
Caused by: org.apache.commons.logging.LogConfigurationException: java.lang.ClassCastException
at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:558)
at org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:345)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:409)
at org.apache.xml.security.Init.<clinit>(Unknown Source)
... 30 more
Caused by: java.lang.ClassCastException
at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:554)
... 33 more
Any help you could give would br greatly appreciated. Thanks, Blaine