SAML Encrypted Response Queries

Explorer ,
Mar 18, 2021 Mar 18, 2021

Copy link to clipboard

Copied

Hi,

 

Just wondering if anyone has any experience of decrypting an EncryptedAssertion from an IDP. My current workflow is:-

 

1. Decrypt the symetric key from EncryptedKey element using RSA and my private key. This is giving me a string like �4Q��2�r ���h` (doesn't look right, or is it?)

2. Use the key extracted above to decrypt the contents of the EncryptedData/CipherData/CipherValue element. This decryption is being done using AES. This is giving me "Invalid AES key length: 28 bytes" error.

 

The decoded xml looks like below:-

<saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
	<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_1244b2f8ed8a27e5454455b4" Type="http://www.w3.org/2001/04/xmlenc#Element">
		<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
		<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
			<ds:RetrievalMethod Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey" URI="#_81c4b6d490243e4e1cae5cf11e73102b"/>
		</ds:KeyInfo>
		<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
			<xenc:CipherValue>VeBwqO/RmOs+aEtIsoZTlE8Gswa6+4z.....</xenc:CipherValue>
		</xenc:CipherData>
	</xenc:EncryptedData>
	
	<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_81c4b6d490fdsfsdfdsf5453f11e73102b">
		<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
		<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
			<xenc:CipherValue>Gbl5+MF8srC2yvQu4A6m2CUCDVu7V1....</xenc:CipherValue>
		</xenc:CipherData>
		<xenc:ReferenceList>
			<xenc:DataReference URI="#_1244b2f8ed8a27e5454455b4"/>
		</xenc:ReferenceList>
	</xenc:EncryptedKey>
</saml2:EncryptedAssertion>

 And my functions to decrypt the key and then the actual data is below:-

 

<cffunction name="decrypt_key" hint="decrypts a base64 encoded string with RSA to its value" access="public" returntype="string" output="false">
		<cfargument name="text" type="string" required="true" hint="the encrypted value as Base64 encoded string" />
		<cfargument name="key" type="any" required="true" />
		<cfargument name="key_type" type="string" default="private" hint="public or private">

		<cfscript>
			var local = structNew();
			/* Create a Java Cipher object and get a mode */
			var cipher = createObject('java', 'javax.crypto.Cipher').getInstance("RSA");
			
            if (NOT isObject(arguments.key)) {
                arguments.key = create_key_object_helper(arguments.key,arguments.key_type);
            }
			
            /* Initialize the cipher with the mode and the key */
            cipher.init(cipher.DECRYPT_MODE, arguments.key);

            /* Perform the decryption */
            local.decrypted = cipher.doFinal(toBinary(arguments.text));

			/* Convert the bytes back to a string and return it */
			return toString(local.decrypted,"UTF-8");
		</cfscript>
	</cffunction>

	<cffunction name="decrypt_string" hint="decrypts a base64 encoded string with AES to its value" access="public" returntype="string" output="false">
		<cfargument name="text" type="string" required="true" hint="the encrypted value as Base64 encoded string" />
		<cfargument name="key" type="any" required="true" />

		<cfscript>
			var local = structNew();
			/* Create a Java Cipher object and get a mode */
			var cipher = createObject('java', 'javax.crypto.Cipher').getInstance("AES/CBC/PKCS5Padding");
			var secretkey = createObject('java', 'javax.crypto.spec.SecretKeySpec').init(toBinary(arguments.key), javaCast( "string","AES"));
			
			/* Initialize the cipher with the mode and the key */
			var iv = createObject('java', 'javax.crypto.spec.IvParameterSpec').init(ToBinary(arguments.text), 0, 16);

            cipher.init(cipher.DECRYPT_MODE, secretkey , iv);

            /* Perform the decryption */
            local.decrypted = cipher.doFinal(toBinary(arguments.text));

			/* Convert the bytes back to a string and return it */
			return toString(local.decrypted,"UTF-8");
		</cfscript>
	</cffunction>

 Can anyone shed some light on where I might be going wrong?

 

Thanks

TOPICS
Advanced techniques, Security

Views

65

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

Enthusiast , Mar 18, 2021 Mar 18, 2021
The key should be 128 bits since the EncryptionMethod is  aes128-cbc, but I think where your problem is is that the cipher.doFinal returns a byte[] (byte array), which is binary, and then you are calling toString(). Try doing an arrayLen() on the result of your cipher.doFinal() value, it should be 16 (16*8 bits per byte of the array = 128). Now instead of doing toString() on that result you probably want to be doing toBase64() which will encode it as a base64 string. You can probably use the bui...

Likes

Translate

Translate
Enthusiast ,
Mar 18, 2021 Mar 18, 2021

Copy link to clipboard

Copied

While you certaily could roll your own SAML implementation in CFML, if you are not a crypto expert I'd recommend using a library like java-saml https://github.com/onelogin/java-saml 

 

I've done a ColdFusion SAML integration with that library, and can recommend it. It takes care of a lot of the hard parts for you. 

 

You could also use the SAML functions in CF2021 which are built on that same java library. 

 

Pete Freitag

Foundeo Inc.

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
Explorer ,
Mar 18, 2021 Mar 18, 2021

Copy link to clipboard

Copied

Hi Pete,

 

I had looked at that library but was thinking it might be too elaborate for what I needed. I did look at some of the functions in there to try and see was I on the right track. I'm certainly no crypto expert and to me I think i'm pretty close to a working solution but I dont know enough to confirm i'm even decrypting the key properly. 

 

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
Enthusiast ,
Mar 18, 2021 Mar 18, 2021

Copy link to clipboard

Copied

The key should be 128 bits since the EncryptionMethod is  aes128-cbc, but I think where your problem is is that the cipher.doFinal returns a byte[] (byte array), which is binary, and then you are calling toString().

 

Try doing an arrayLen() on the result of your cipher.doFinal() value, it should be 16 (16*8 bits per byte of the array = 128).

 

Now instead of doing toString() on that result you probably want to be doing toBase64() which will encode it as a base64 string. You can probably use the builtin decrypt() function at this point, instead of your decrypt_string() function - you just need to make sure you are using CBC mode with AES again. 

 

Ok, I might have said too much 😉 still need to be careful a bunch of things: https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html 

 

Pete Freitag

Foundeo Inc.

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
Explorer ,
Mar 18, 2021 Mar 18, 2021

Copy link to clipboard

Copied

LATEST

Hi Pete,

 

Thanks for all that I eventually got it working. I did use decrypt instead of the other function I had created. I had at some point tried to use that but went back to my other function. The one issue I had was with the algorithim. I got an error until I used "AES/CBC/NoPadding". 

 

Thank again

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