Deciphering a SAML Message in ColdFusion

Engaged ,
Sep 18, 2014 Sep 18, 2014

Copy link to clipboard

Copied

I'm working on an SSO solution for a client.  At this time I'm able to encode an authentication message and successfully send it to the ADFS server.  The ADFS server handles my login and then returns to my site with an HTTP-POST response.  In the POST there is an ADFS encoded SAML message I need to decipher.  I found a few samples of code but none have worked.  This one seemed to have the most promise but...

<cfscript>

// Decode the query string from Base 64 

  Decoder = CreateObject("Java", "sun.misc.BASE64Decoder").init();

  SamlByte = Decoder.decodeBuffer(Form.SAMLResponse);

// Create Byte Array used for the inflation, the CF way 

  ByteClass = CreateObject("Java", "java.lang.Byte").TYPE;

  ByteArray = CreateObject("Java", "java.lang.reflect.Array").NewInstance(ByteClass, 1024);

// Create Byte Streams needed for inflation

  ByteIn   = CreateObject("Java", "java.io.ByteArrayInputStream").init(SamlByte);

  ByteOut  = CreateObject("Java", "java.io.ByteArrayOutputStream").init();

// Create Objects needed for inflation 

  Inflater = CreateObject("Java", "java.util.zip.Inflater").init(true);

  InflaterStream = CreateObject("Java", "java.util.zip.InflaterInputStream").init(ByteIn, Inflater);

// Complete the inflation 

  Count = InflaterStream.read(ByteArray);

  while (Count != -1) {

  ByteOut.write(ByteArray, 0, Count);

  Count = InflaterStream.read(ByteArray);

  }

// Finished with inflation 

  Inflater.end();

  InflaterStream.close();

// Convert SAML request back to a string 

  SamlString = CreateObject("Java", "java.lang.String").init(ByteOut.toByteArray());

  </cfscript>

When the code get to the Count = InflaterStream.read(ByteArray); statement the following error message is returned: oversubscribed dynamic bit lengths tree

My question is does anybody have a snippet of code that is used to successfully decipher an ADFS encoded SAML response?

Views

3.0K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct Answer

Engaged , Nov 21, 2019 Nov 21, 2019
For any other ColdFusion dev who happens upon this posting....Of all the relatively few articles I could find for ColdFusion and SAML none of them were working for us.  But, they all gave hints and clues as to what would work.  I think a lot of our trouble was due to differences in the encoded response we were getting from our IDP compared with many of the other (much older) postings we read.Anyway, we were receiving an encoded response from an Azure AD.  Here's how we ended up decoding it suces...

Likes

Translate

Translate
New Here ,
May 16, 2016 May 16, 2016

Copy link to clipboard

Copied

I'm working on the same problem.  I'm using this function, but it doesn't work either.  I've figured out that the problem is Java's gzip file is valid (it has a 10-byte header, and a 8-byte trailer), but the ADFS one is missing both of those (it only contains the content).  while it's easy to chop off the the first 10 bytes and the last 8-bytes to send ADFS what it's expecting, it's not possible (as far as I can tell) to derive the last 8 bytes without having the decompressed file.

<cffunction name="ungzip" returntype="string">       <cfargument name="encodedString" type="string" required="true">       <cfargument name="encoding" type="string" default="Base64">       <cfscript>            var line = "";            if( ListContains("Base64,Hex,UU", encoding) ) {                 var data = BinaryDecode(encodedString, encoding);                  var buffReader = createObject("java", "java.io.BufferedReader").init(                                         createObject("java", "java.io.InputStreamReader").init(                                             createObject("java", "java.util.zip.GZIPInputStream").init(                                                 createObject("java", "java.io.ByteArrayInputStream").init(data)                                             )                                         )                                     );                 line = buffReader.readLine();                 buffReader.close();            }             return line;       </cfscript>  </cffunction>

Did you ever get something figured out?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
May 16, 2016 May 16, 2016

Copy link to clipboard

Copied

I found this code, which works.  Reading, decoding and inflating a SAML XML respone with Coldfusion · GitHub

The key here is using InflateInputStream, and specifying the Inflater with the nowrap parameter set to true, so it leaves out the header and footer info I mentioned above.

[edit:] Looking at the code you posted, it's the same, except for how you're converting the base64 string to binary.  Use coldfusion's built in function for this:

SamlByte = ToBinary(Form.SAMLResponse);

or

SamlByte = BinaryDecode(Form.SAMLRespone, "Base64");

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Nov 19, 2019 Nov 19, 2019

Copy link to clipboard

Copied

Wondering if you have any tips for us.  We're trying to do exactly what you were doing but we're having trouble deciphering the response. We're trying to follow what little info there is for ColdFusion with SAML and are working with the CFC you pointed out here:

https://gist.github.com/guillaumemolter/3e210855881ec5f09294

 

But, similar to above, when we get to

InflaterStream.read(ByteArray);

we are getting the error:

"invalid stored block lengths"

 

Any ideas?

 

<cfdump var="#decodeSAMLResponse(toBase64(SAMLResponse))#">

<cffunction name="decodeSAMLResponse" output="true">
	<cfargument name="SAMLResponse" required="true" hint="a base64 encoded, deflated SAML response string">

		<!--- We convert the string to binary and a ByteArrayInputStream to be able to read the stream --->
	<cfset byteIn = createObject("java", "java.io.ByteArrayInputStream").init(ToBinary(arguments.SAMLResponse)) />
	<cfset byteClass = CreateObject("Java", "java.lang.Byte").TYPE  />
	<cfset byteArray = CreateObject("Java", "java.lang.reflect.Array").NewInstance(byteClass, 1024) />
	<!--- We init the output object where we are going to inflate our response --->
	<cfset byteOut  = CreateObject("Java", "java.io.ByteArrayOutputStream").init() />
	<!--- We init the zip inflater --->
	<cfset inflater = CreateObject("Java", "java.util.zip.Inflater").init(true)  />
	<!--- We are specifying to the ibnflater what to read  --->
	<cfset inflaterStream = CreateObject("Java", "java.util.zip.InflaterInputStream").init(byteIn, inflater) />

	<!--- We loop through our byte array, inflate the buffer and store it inside of our output buffer --->
	<cfset Count = inflaterStream.read(byteArray) />
	<cfloop condition="Count neq -1">
		<cfset byteOut.write(byteArray, 0, Count) />
	    <cfset Count = inflaterStream.read(byteArray) />
	</cfloop>

	<!--- We end/close our inflater --->
	<cfset inflater.end() />
	<cfset inflaterStream.close() />

	<!--- Finally we convert back our bytes stream to a nice string 🙂 --->
	<cfset inflatedSAMLResponse = ToString(byteOut.tobyteArray()) />

	<cfreturn inflatedSAMLResponse>

</cffunction>

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Nov 21, 2019 Nov 21, 2019

Copy link to clipboard

Copied

For any other ColdFusion dev who happens upon this posting....

Of all the relatively few articles I could find for ColdFusion and SAML none of them were working for us.  But, they all gave hints and clues as to what would work.  I think a lot of our trouble was due to differences in the encoded response we were getting from our IDP compared with many of the other (much older) postings we read.

Anyway, we were receiving an encoded response from an Azure AD.  Here's how we ended up decoding it sucessfully.  Hope it helps someone.

<cffunction name="decodeSAMLResponse" output="false" hint="Decode a SAML authentication response from Azure AD">
	<cfargument name="SAMLResponse" required="true">

	<cfset cleanSAMLResponse = replaceNoCase(arguments.SAMLResponse,"SAMLResponse=","","all")>
	<cfset xmlStr = createObject("java", "javax.xml.bind.DatatypeConverter").parseBase64Binary(base64urldecode(cleanSAMLResponse)) />
	<cfset xmlStr = ToString(xmlStr,"utf-8")>

	<!--- Soemtimes a few xtra characters after the closing tag --->
	<cfset cnt = find("</samlp:Response>",xmlStr)>
	<cfif cnt GT 0 AND len(xmlStr) GT cnt+16>
		<cfset xmlStr = left(xmlStr,cnt+16)>
	</cfif>
	<cfreturn xmlStr>
</cffunction>
<cffunction name="base64urldecode" output="true" hint="The Azure response contained a few URL encodings that weren't getting converted properly by 'normal' CF URL decode functions.">
	<cfargument name="SAMLResponse" required="true">

    <cfset value = replace(arguments.SAMLResponse, "%2B", "+", "all" )>
    <cfset value = replace(value, "%3D", "=", "all" )>
    <cfreturn value>
</cffunction>

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 07, 2020 Feb 07, 2020

Copy link to clipboard

Copied

LATEST

I don't know who sdsinc_pmascari is, but if you're ever at CF Summit, let me buy you dinner! Your updated solution worked great! Decoding/encoding is not my strong suit. I dread any project that involves it. Thanks!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines