Using ColdFusion to Deflate compress a SAML XML authentication message
I am working on a ColdFusion project that uses SAML to authenticate users. I won't get into all the specific application details as they are quite complicated. However, the point of this blog post is to explain the components of the authentication request XML message that gets DEFLATE compressed, Base64 encoded and URL encoded. I hope some of this can help others dealing with SAML and/or Deflate compression in their projects.
To implement "DEFLATE compressed" I knew Java would come into play and I found this resource which explained the Java Deflater class.
This is the example Java code I needed to represent in ColdFusion.
String inputString = "blahblahblah??";
byte[] input = inputString.getBytes("UTF-8");
/* Compress the bytes */
byte[] output = new byte[100];
Deflater compresser = new Deflater();
compresser.setInput(input);
compresser.finish();
int compressedDataLength = compresser.deflate(output);
I had a hard time finding any help on how I could create the Java byte array "output" variable in ColdFusion. Luckily, I came across this old 2004 blog post from Christian Cantrell. This find was a life saver, thanks Christian! Here is Christian's code:
<cfargument name="size" type="numeric" required="true"/>
<cfset var emptyByteArray = createObject("java", "java.io.ByteArrayOutputStream").init().toByteArray()/>
<cfset var byteClass = emptyByteArray.getClass().getComponentType()/>
<cfset var byteArray = createObject("java","java.lang.reflect.Array").newInstance(byteClass, arguments.size)/>
<cfreturn byteArray/>
</cffunction>
Here is my final ColdFusion code that represents the original Java example above.
<cfset emptyByteArray = createObject("java", "java.io.ByteArrayOutputStream").init().toByteArray()/>
<cfset byteClass = emptyByteArray.getClass().getComponentType()/>
<cfset output = createObject("java","java.lang.reflect.Array").newInstance(byteClass, 500)/>
<!--- perform Deflate, Base64 encode, and URL encode --->
<cfscript>
saml_deflate = createObject("java", "java.util.zip.Deflater");
saml_deflate.init(9,true);
saml_deflate.setInput(saml_xml.getBytes("UTF-8"));
saml_deflate.finish();
compressedDataLength = saml_deflate.deflate(output);
data64 = toBase64(output,"UTF-8");
data64url = urlencodedformat(data64);
</cfscript>
Before the ColdFusion code above, you start with the SAML XML message like this, which is saved in the CF variable "saml_xml", and the final result is in the CF variable "data64url".
<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="a966242f393474b7d95f4581ff7db131d" Version="2.0"
IssueInstant="2010-05-10T16:24:33.525Z">
<saml:Issuer>http://theissuer.domain.org/app/</saml:Issuer>
</samlp:AuthnRequest></cfsavecontent>






