6

We've got a Silent Login service written in Coldfusion9 that accepts encrypted strings from external systems and then decrypts based on an agreed Algorithm/Encoding setup. This has worked without issue for years now from systems running ASP/JAVA/PHP, but we now have a client who has no choice but to use CryptoJS to perform the encryption and for the life of me I cannot work out why this won't decrypt in Coldfusion.

My knowledge of encryption isn't brilliant but the thing I am noticing is the CryptoJS encrypted ciphertext for the exact same string/key differs every time i perform the encryption whereas in Coldfusion/Java i can always expect the exact same encrypted string. I'm not sure if this is encoding related or not but i've never run into this issue accepting encrypted strings from any other system before, so I am hoping it's the way I am encrypting in CryptoJS that is incorrect.

<cfoutput>

<!--- Set String and Key --->
<cfset theKey = toBase64("1234567812345678")>
<cfset string = "max.brenner@google.com.au">

<!--- CryptoJS AES Libraries --->
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>

<script>

// Encrypt String using CryptoJS AES
var encrypted = CryptoJS.AES.encrypt("#string#", "#theKey#");
console.log(encrypted.toString());

// Decrypt String using CryptoJS AES 
var decrypted = CryptoJS.AES.decrypt(encrypted, "#theKey#");
console.log(decrypted.toString(CryptoJS.enc.Utf8));

</script>

<!--- Coldfusion Decrypt String / FAILS --->
Decrypted: #decrypt(encryptedEmail, "#theKey#", "AES", "BASE64")#

</cfoutput>
Phil Rasmussen
  • 635
  • 1
  • 7
  • 20
  • your sample code doesn't run because you can't mix JavaScript and ColdFusion variables together like you are. JavaScript is client side and ColdFusion is server side, they can't talk how you're trying to make them work. – Matt Busche May 17 '13 at 02:58
  • Matt when this script is being run as a CFM and parsed/compiled by Coldfusion then these variables will be evaluated before the javascript is run. I've worked on plenty of projects over the years where coldfusion and javascript are exchanging data, I think the issue here is related to the encoding in CryptoJS and me approaching this from a simplistic angle. – Phil Rasmussen May 17 '13 at 06:55
  • Encryptedemail isn't defined anywhere. That's not your overall issue, but the sample code doesn't give the error you say it does – Matt Busche May 17 '13 at 11:10
  • 3
    "_the thing I am noticing is the CryptoJS encrypted ciphertext for the exact same string/key differs every time i perform the encryption_" I think this is because you are not passing the key as a WordArray to CryptoJS so it is building one for you each time you call it. [CryptoJS Custom Key and IV](https://code.google.com/p/crypto-js/#Custom_Key_and_IV) and [another reference for you](http://stackoverflow.com/questions/14958103/how-to-decrypt-message-with-cryptojs-aes-i-have-a-working-ruby-example) – Miguel-F May 17 '13 at 14:06
  • Sorry for the late reply Miguel that makes sense and after reading the full response from Leigh below i've learnt a lot more about encryption now thankyou guys. – Phil Rasmussen May 22 '13 at 01:25

1 Answers1

13

There seem to be two issues:

  1. CryptoJS is not using your variable as the key. As @Miguel-F mentioned, when you pass in a string, "it's treated as a passphrase and used to derive [the] actual key and IV". Both are randomly generated, which is why your encrypted result keeps changing. But more importantly, this means that CryptoJS is using a completely different key than the one in your CF code and that is why decrypt() fails. (At least it is part of the reason ...)

  2. The second problem is that in addition to the algorithm "AES", there are two other encryption settings which must match: mode and padding scheme. While CryptoJS and ColdFusion use the same defaults for padding scheme, the "modes" are different:

You need to ensure all three settings are the same on both sides. Try using CBC mode in CF, since it is more secure than ECB anyway. Note: It requires adding an IV value.

CF Code:

<!--- this is the base64 encrypted value from CryptoJS ---> 
<cfset encrypted = "J2f66oiDpZkFlQu26BDKL6ZwgNwN7T3ixst4JtMyNIY=">
<cfset rawString = "max.brenner@google.com.au">
<cfset base64Key = "MTIzNDU2NzgxMjM0NTY3OA==">
<cfset base64IV = "EBESExQVFhcYGRobHB0eHw==">

<cfset ivBytes = binaryDecode(base64IV, "base64")>
<cfoutput>
    #decrypt(encrypted, base64Key, "AES/CBC/PKCS5Padding", "base64", ivBytes)#
</cfoutput>

CryptoJS: (Adjusted Original Example)

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
<script>
    var text = "#rawString#";
    var key = CryptoJS.enc.Base64.parse("#base64Key#");
    var iv  = CryptoJS.enc.Base64.parse("#base64IV#");

    var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv});
    console.log(encrypted.toString());

    var decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv: iv});
    console.log(decrypted.toString(CryptoJS.enc.Utf8));
</script>


Edit:

All that said, what do you mean by the client "has no choice but to use CryptoJS to perform the encryption"? Why cannot they use server side encryption? I am not an encryption expert, but doing encryption in javascript, and exposing the key on the client, does not sound wildly secure to begin with ...

Leigh
  • 28,765
  • 10
  • 55
  • 103
  • +1 @Leigh - I had been looking at it this morning (but then had to leave) and basically came to the same conclusion as you. Without all the same pieces ColdFusion will not be able to decrypt the value. I even wrote up a test script similar to what you have here but never did get it to decrypt correctly... – Miguel-F May 17 '13 at 19:50
  • Yeah, it usually something small like a difference like encoding, mode or padding. But with encryption that is all it takes to get a completely different result ;-) @Phil - For clarity's sake, you may want to set the CryptoJS mode and padding explicitly ie `{iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}`. Just so it is clear. – Leigh May 17 '13 at 20:24
  • @Miguel-F - For grins I tried using the less secure `ECB` mode too. But had no luck with it. The docs say it is supported, but I am not so sure .. because they also say *"If you pass the actual key, you must also pass the actual IV."*. Given that ECB does not use an `iv` that does not make sense .. No great loss though, since CBC is better anyway. – Leigh May 17 '13 at 20:29
  • Funny that you say that because I was trying ECB mode. At least that makes me feel better that I could not get it to work. ;) – Miguel-F May 18 '13 at 00:00
  • Thankyou so much for taking the time to write this answer Leigh worked like a charm and i learnt a little more about encryption in the process and the specific modes as I had never used CBC and the IV's before. Can i just confirm that the IV and Key you generated can be done with the Coldfusion GenerateSecretKey('AES') for both. I looks like an AES key but just wanted to confirm. – Phil Rasmussen May 22 '13 at 01:27
  • Yes. As long as CryptoJS follows the specs for AES (which it appears to) the keys are interchangeable. Just keep in mind that doing *anything* client side is less secure, because you have to expose the data for it to work. In this case they key. So it seems a little like locking your house, but leaving the key taped to the front door. – Leigh May 22 '13 at 13:09
  • To generate `iv` values, see java's [SecureRandom](http://stackoverflow.com/questions/1785555/how-should-i-generate-an-initialization-vector) – Leigh May 22 '13 at 13:49
  • @Leigh Is there anyway i can compress the string before encyption? – Rajeev Dec 26 '13 at 17:42
  • [Demo Link for the above source Code: Plnkr link : Look for browser console for result](https://plnkr.co/edit/IF3afz) – Abhijeet Nov 08 '16 at 06:29
  • Why does adding or removing a character from `"#base64Key#"` make this fail? – André C. Andersen Nov 10 '19 at 22:52
  • or you can replace enc.Base64 with enc.Hex – Fergus Mar 28 '21 at 04:07