13

I'm trying to encode/decode data using CryptoJS, as a preliminar test for the code I want to develop. This is the code I'm using for encrypting:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var message = "Secret Message";
var key = CryptoJS.enc.Hex.parse('36ebe205bcdfc499a25e6923f4450fa8');
var iv  = CryptoJS.enc.Hex.parse('be410fea41df7162a679875ec131cf2c');

// Encription. Works ok
var encrypted = CryptoJS.AES.encrypt(
        message,key,
        {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
    );
console.log('encrypted:'+encrypted.ciphertext.toString());
<script>

This is the first test I use for decrypting. It works OK, returning 3f0e590d2617dc7007b89350bd590409

// Decription. Works ok with "encrypted" parameter
var decrypted = CryptoJS.AES.decrypt(
        encrypted,key,
        {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
    );
console.log('decrypted:'+decrypted.toString(CryptoJS.enc.Utf8));

Let's notice that encrypted parameter is the results from the previous call to CryptoJS.AES.encrypt. It's an object.

The problem I have is when I try to decrypt directly the string:

// Decription. It fails with manual data
var manual_data = CryptoJS.enc.Hex.parse('3f0e590d2617dc7007b89350bd590409');
var decrypted = CryptoJS.AES.decrypt(
        manual_data,key,
        {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
    );
console.log('decrypted, by hand:'+decrypted.toString(CryptoJS.enc.Utf8));

It returns an "empty" object (an empty string in the above example). It seems like there is some data that CryptoJS.AES.decrypt needs which is stored into the encrypted object of the first example but missing from the wordarray of the second example.

Does anybody knows why is this happening?

Alvaro Maceda
  • 604
  • 1
  • 7
  • 21

2 Answers2

23

I have been messing with this a while and I think I have found your problem. The main problem is this line encrypted.ciphertext.toString(). What you want is just encrypted.toString().

The toString function is defined for this object by CryptoJS and it returns the encrypted message that can be sent around safely. So if we change that we will have something like this:

var encrypted = CryptoJS.AES.encrypt(
  message,
  key,
  {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  }
);
console.log('            encrypted: '+encrypted.toString());

This will output Pw5ZDSYX3HAHuJNQvVkECQ== instead of 3f0e590d2617dc7007b89350bd590409. The reason your second function is working is because it doesn't use encrypted.ciphertext.toString() it just uses the actual object so no changes on that one. For the last one we will have the change the wrong text you were using to the new text that is returned without the ciphertext part but we also have to remove the CryptoJS.enc.Hex.parse. I don't really know what you were doing here but I can investigate if you meant something by that.

var manual_data = 'Pw5ZDSYX3HAHuJNQvVkECQ==';
var decrypted = CryptoJS.AES.decrypt(
  manual_data,
  key,
  {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  }
);
console.log('   decrypted, by hand: '+decrypted.toString(CryptoJS.enc.Utf8));

This should log the right stuff.

I have even created a JSBin for this. It is my first time using JSBin so I hope I did it right.

DutGRIFF
  • 5,103
  • 1
  • 33
  • 42
  • JSBin was quickie... It did the job. – jeet.chanchawat Feb 15 '17 at 10:51
  • what if I actually need to use the ".ciphertext." part because of remote part I'm talking to only sends the hex-encoded string? I've been struggling with this for a week and I could not find a way to rebuild the WordArray starting from the ciphertext. – afe Nov 19 '18 at 09:04
  • according to the documentation in the typescript the default value is the hex encoding, when you call encrypted.ciphertext.toString() it returns it as a hex string, and you need to call CryptoJS.enc.Hex.parse() to convert it to a CryptoJS.lib.WordArray and then call CryptoJS.enc.Base64.stringify() on the new word array before you can pass it into the CryptoJS.AES.decrypt otherwise, you'll end up with an empty string. – Lawrence LCI Mar 08 '22 at 19:49
2

If you still prefer to use the ciphertext, by default the when no encoding is passed into the ciphertext.toString(), it'll return a CryptoJS.enc.Hex.stringify on the ciphertext WordArray.

Since decrypt takes in text as a base64 string, you will need to call the following on your ciphertext hex string before you pass it back into decrypt.

CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(encrypted.ciphertext.toString()))

so in the case,

var manual_data = '3f0e590d2617dc7007b89350bd590409';
var base64_data = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(manual_data))
var decrypted = CryptoJS.AES.decrypt(
  base64_data,
  key,
  {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  }
);
console.log('decrypted by hand: '+decrypted.toString(CryptoJS.enc.Utf8));
Dharman
  • 30,962
  • 25
  • 85
  • 135
Lawrence LCI
  • 71
  • 1
  • 4