Decode SAML Request

2,896 views
Skip to first unread message

Ryan Panning

unread,
Jul 12, 2011, 10:49:13 AM7/12/11
to google-app...@googlegroups.com
I just wanted to change the title (I feel like "ColdFusion" will limit some from reading the message) and give more details.
 
So I'm trying to decode/deflate/decrypt the SAML Request that comes from Google. Since the Response should be Base64 encoded before submitted to Google, I assumed that the Request is Base64 encoded as well. That doesn't seem to be the case because I Base64Decode and still get garbage. Then I read on another thread here to just URLDecode the Request, but that doesn't work either. I know it is possible because using the SimpleSAML debugger Webpage works (https://rnd.feide.no/simplesaml/module.php/saml2debug/debug.php). There must be something more than Base64 encoding going on... Here is what I've tried so far with no luck (this is ColdFusion but the solution could be Java too). ANY help/suggestions/input would be great. Thanks!!
 
<!--- The URLDecode as suggested here --->
<cfdump var="#URLDecode(URL.SAMLRequest)#" />
 
<!--- Standard Base64 decoding with CF --->
<cfdump var="#ToString(ToBinary(URL.SAMLRequest))#" />
 
<!--- Java Base64Decode Sun library --->
<cfset decoder=CreateObject("Java", "sun.misc.BASE64Decoder") />
<cfdump var="#ToString(decoder.decodeBuffer(URL.SAMLRequest))#" />

Ryan Panning

unread,
Jul 12, 2011, 1:08:00 PM7/12/11
to google-app...@googlegroups.com
Ok, I'm getting closer. After doing some more reading it sounds like the SAML Request from Google is Deflated, Base64 Encoded, and URLEncoded. So I've been working on reversing the order to get the original XML. I'm to the point of Inflating but am getting a "unknown compression method" from Java. How is Google Deflating the request? Thanks again for any help.
 
<cfscript>
 
// Decode the string from Base 64, CF already URL Decoded it
SamlReq = ToString(ToBinary(URL.SAMLRequest));
 
// Create Byte Array used for the inflation
ByteClass = CreateObject("Java", "java.lang.Byte").TYPE;
ByteArray = CreateObject("Java", "java.lang.reflect.Array").NewInstance(ByteClass, 1024);
 
// Complete the actual Inflation
JInflater = CreateObject("Java", "java.util.zip.Inflater");
JInflater.SetInput(SamlReq.GetBytes());
JInflater.Inflate(ByteArray); // ERROR HERE
JInflater.end();
 
// TODO: Convert ByteArray back to string
 
</cfscript>

Scott Spyrison

unread,
Jul 12, 2011, 11:00:05 PM7/12/11
to SAML-based Single Sign On for Google Apps
Hi Ryan,

If you're looking to implement yourself, this is a good place to
start:

http://code.google.com/googleapps/domain/sso/saml_reference_implementation_web.html

best,
scott

Claudio Cherubino

unread,
Jul 12, 2011, 11:35:03 PM7/12/11
to google-app...@googlegroups.com
Hi Scott,

Actually, that is not a good reference.
As it's written on top of the page, that code is now deprecated and no longer compatible with Google Apps.
If you want to study the source code of a good SAML implementation I'd recommend SimpleSAMLphp or Shibboleth.

Claudio

--
You received this message because you are subscribed to the Google Groups "SAML-based Single Sign On for Google Apps" group.
To post to this group, send email to google-app...@googlegroups.com.
To unsubscribe from this group, send email to google-apps-saml...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-apps-saml-sso?hl=en.


Ryan Panning

unread,
Jul 13, 2011, 10:00:32 AM7/13/11
to google-app...@googlegroups.com
Actually after reviewing the out-dated Web page I did notice the source code link. But, after finding the correct peice of code, it turns out that I'm doing the same thing. So, yes that document is out-dated and doesn't work. I have tried to dig through the Shibboleth source but couldn't find the right file. At this point I'm at a different error, "incomplete dynamic bit lengths tree". Here is what I'm at so far:
 
<cfscript>

// Decode the string from Base 64, CF already URL Decoded it
SamlReq = ToString(ToBinary(URL.SAMLRequest));

// Create Byte Array used for the inflation
ByteClass = CreateObject("Java", "java.lang.Byte").TYPE;
ByteArray = CreateObject("Java", "java.lang.reflect.Array").NewInstance(ByteClass, 5000);


// Complete the actual Inflation
JInflater = CreateObject("Java", "java.util.zip.Inflater").init(true);
JInflater.SetInput(SamlReq.GetBytes("UTF-8"));
JInflater.Inflate(ByteArray);

JInflater.end();

// TODO: Convert ByteArray back to string
</cfscript>

Scott Spyrison

unread,
Jul 14, 2011, 8:45:57 AM7/14/11
to google-app...@googlegroups.com
Hi Claudio & Ryan,

Yes that code is deprecated as indicated, and absolutely not fit for production for many reasons.  The value in the deprecated code that Google has left up there is as a learning tool.  It's fantastic for building as a war, deploying to Tomcat, and using as a tutorial or demo.

My fault - I didn't read the original post carefully enough - that particular part of Google's code doing the decoding and inflating is one of the more significant issues with their implementation.  It periodically hits that buffer of 5000 bytes and fails.  I encountered that myself when I deployed the code as a "SAML Demo," and I read a few other posts about this:


which seems to reference this:


but I like Claudio's idea of referencing Shib to see how they implemented the decoding and inflating.

best,
scott

Ryan Panning

unread,
Jul 14, 2011, 2:38:41 PM7/14/11
to google-app...@googlegroups.com
It's working!!! This has been one of those issues that drags you down, thinking it's not possible. Now I'm so excited that it's working, when it's something that should've worked from the begining.
 
Anyway, so it turns out that the "ColdFusion" way of decoding Base64 wasn't working right so I used a Java class instead. It is a "unsupported" Sun class that might go away so that might be an issue, but it looks like there are alternatives too. Then I changed how the Inflation works to avoid that buffer limit. In case anyone is interested, here is the code that works for me to decode a GoogleApps SAML Request. Thanks again, some of these links here helped in the right direction!
 
<cfscript>

// Decode the query string from Base 64
Decoder = CreateObject("Java", "sun.misc.BASE64Decoder").init();
SamlByte = Decoder.decodeBuffer(URL.SAMLRequest);

// 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>

Ryan Panning

unread,
Jul 14, 2011, 2:51:19 PM7/14/11
to google-app...@googlegroups.com
Sorry for the malformed code above, I've attached it in a txt file instead.
 
~ Ryan
CF_Decode_SAML_Request.txt
Reply all
Reply to author
Forward
0 new messages