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?
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
...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?
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");
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>
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>
Copy link to clipboard
Copied
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!