1

I am having some trouble decrypting data using CryptoJS that was encrypted in PHP. Maybe somebody can advise me on where I am going wrong?

I am encrypting as follows:

  1. Get hashed password
  2. Take substring of (0,16) as the key
  3. Encrypt (MCRYPT_RIJNDAEL_128)
  4. Encode ciphertext as base64

When decrypting I do the same:

  1. Get hashed password
  2. Take substring of (0,16) as the key
  3. Base64 decode the ciphertext
  4. Decrypt

PHP:

public function encrypt($input, $key) {
    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $input = $this->_pkcs5_pad($input, $size);
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $data = base64_encode($data);
    return $data;
}

JavaScript:

function decrypt(ciphertext, hashedPsw) {
        var key =  hashedPsw.substring(0, 16);

        var key = CryptoJS.enc.Hex.parse(key);

        var options = { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, keySize:128 / 32 };

        ciphertext = CryptoJS.enc.Base64.parse(ciphertext);
        var decrypted = CryptoJS.AES.decrypt(ciphertext, key);
        return decrypted;
    }
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
JB2
  • 1,587
  • 2
  • 24
  • 40
  • 1
    Just out of curiosity are you using `https` as well or only this method? –  Jun 11 '15 at 12:57
  • Yes, I am using https as well :) – JB2 Jun 11 '15 at 13:01
  • I'm kind of new to encryption maybe that is why I don't understand why you need both? If you are using `https` then all of your data is encrypted before it leaves the browser or server. That is, there is no plaintext on the "wire" –  Jun 11 '15 at 13:04
  • I found a solution in a previous post: [here][1]. [1]: http://stackoverflow.com/questions/24337317/encrypt-with-php-decrypt-with-javascript-cryptojs – JB2 Jun 11 '15 at 23:55

2 Answers2

4

The CryptoJS decrypt function expects an object that contains a WordArray and not the WordArray itself, so you need to use:

var decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertext }, key, options);

You also need to pass the options to the decrypt function. Otherwise, CryptoJS won't know that you wanted to use ECB mode.


Security

Don't use ECB mode! It's not semantically secure. You should at the very least use CBC mode with a random IV. The IV doesn't need to be secret, so you can simply prepend it to the ciphertext.

Then you should authenticate your ciphertexts. This can be done with authenticated modes like GCM or EAX, but they are not provided by mcrypt or CryptoJS. The next best thing is to use an encrypt-then-MAC scheme where you use a strong keyed hash function like HMAC-SHA256 over the ciphertext to make it infeasible for an attacker to change ciphertexts without you knowing it.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • 1
    Incase this doesn't solve your problem, you should provide a full encryption-decryption cycle as code with example data and key. – Artjom B. Jun 11 '15 at 14:21
  • Thanks for the answer and info. I had added the options (but forgot to show so in the code here). Unfortunately I am still having problems. I now get: TypeError: Cannot read property '0' of undefined at Object.CryptoJS.lib.Cipher.x (aes.js:26) – JB2 Jun 11 '15 at 15:42
  • 1
    Now you have 4 new bugs: the first one is EBC typo. Now, take a little time look at how you encode think an whether it matches between php and js. – Artjom B. Jun 11 '15 at 16:35
  • Uups - fixed ECB, fixed the iv base64 encoding, fixed the UTF-8 stringify...and removed the pks5 padding from the PHP code (PKSC7 are the default apparently)...But the problem persists... – JB2 Jun 11 '15 at 17:21
  • Again, you haven't shown the full code (HTML). Have you included the ECB component for CryptoJS? Because it's not included in the AES roll up by default. If you haven't, just switch to CBC mode, because you have everything on both sides for that. – Artjom B. Jun 11 '15 at 17:26
  • Sorry, and thank you! - I edited the post to show the includes. I am receiving the ciphertext via a REST API. I changed from ECB to CBC, and also removed the Hex decoding. Now it works without errors, but doesn't give the plaintext... – JB2 Jun 11 '15 at 17:43
  • (the initial problem was resolved by switching to CBC - I had just included the default rollup). I think the problem is related to the encoding. – JB2 Jun 11 '15 at 17:51
  • You still haven't shown example inputs and outputs. How is the key encoded? Is it a WordArray already (probably not)? Is it a hex string (if yes, then it should be parsed)? – Artjom B. Jun 11 '15 at 22:27
  • Thanks for the help. In fact, I just discovered the answer here: http://stackoverflow.com/questions/24337317/encrypt-with-php-decrypt-with-javascript-cryptojs Turns out that the problem was the key encoding – JB2 Jun 12 '15 at 19:41
0

I just discovered the answer in a previous thread: Turns out that the problem was the key encoding.

Community
  • 1
  • 1
JB2
  • 1,587
  • 2
  • 24
  • 40
  • This answer is entirely unhelpful. What was the actual issue? Which encoding was used in PHP and which in CryptoJS? You would need to add these details to the question so that this answer makes any sense. But then, the question is a completely different one from what it started which should never have happened. I rolled back your question, because the question should not be changed if there are already answers. You can provide the code that you ended up using in this answer. – Artjom B. Jun 12 '15 at 19:51