1

I encrypt and decrypt data using PHP like this:

<?php 
function encrypt($data, $secret){
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    return base64_encode($iv.openssl_encrypt($data, 'aes-256-cbc', $secret, 0, $iv));
}

function decrypt($encryptedData, $secret){
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $data = base64_decode($encryptedData);
    $iv = substr($data, 0, $iv_size);
    return openssl_decrypt(substr($data, $iv_size), 'aes-256-cbc', $secret, 0, $iv);
}
?>

I am now wanting to be able to encrypt my data locally (identically to the PHP method) using Crypto-JS. I have done the same as above to get the key and iv:

var key = '<?php echo $secret;?>';
var iv = '<?php echo base64_encode(mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND));?>';

Now when using Crypto-JS I have tried to encrypt using:

var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(text), CryptoJS.enc.Hex.parse(key), { iv: CryptoJS.enc.Hex.parse(iv) });

But I also need to store the IV like I do with PHP So I have added:

var withIV = iv+encrypted;

but that is not encoded. So I have added:

CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(withIV));

But this is not the same encoding as the PHP above for some reason?

maxisme
  • 3,974
  • 9
  • 47
  • 97
  • don't dump text from php into a json context directly. while the b64 stuff is reasonably safe, that key could contain js metachars. you should be using json_encode: `var foo = ;` so you're ALWAYS generating valid javascript. – Marc B Mar 09 '16 at 18:49
  • @MarcB would you be able to link me to something with more info on what you are talking about? I haven't got the foggiest why what I am doing is bad. :( – maxisme Mar 09 '16 at 18:52
  • php: `$name = "Miles O'Brien";`, which you then echo into JS: `var js = 'Miles O'Brien';` - oops, syntax error, and now your js code block is dead. using json_encode would produce the proper/valid `var js = 'Miles O\'Brien';` – Marc B Mar 09 '16 at 18:55
  • Ahhh!! Sorry stupid of me! I will start doing that. Any ideas on the encoding question above? – maxisme Mar 09 '16 at 19:10

2 Answers2

0

Here's how I encrypt data with CryptoJS:

function encrypt(str, key, iv) {
    var key = CryptoJS.enc.Hex.parse(key);
    var iv = CryptoJS.enc.Hex.parse(iv);
    return CryptoJS.AES.encrypt(str, key, { iv: iv }).toString();
};

In PHP, I decrypt the encrypted string produced by that function using this line of code:

openssl_decrypt($encrypted_data_string, "AES-128-CBC", hex2bin($key_hex_string), 0, hex2bin($iv_hex_string));

I suppose you could encode/decode the encrypted data in base 64 instead of hexadecimal if you wanted to. Anyway, hope this helps!

0

It seems you have trouble concatenating IV and the ciphertext in CryptoJS. This is rather easy, because CryptoJS' native binary data format (WordArray) supports the concat function:

var ivWords = CryptoJS.enc.Hex.parse(iv); // WordArray instance
var plaintext = CryptoJS.enc.Utf8.parse(text); // WordArray instance
var keyWords = CryptoJS.enc.Hex.parse(key); // WordArray instance
var encrypted = CryptoJS.AES.encrypt(plaintext, keyWords, { iv: ivWords }); // CipherParams instance

var ct = ivWords.clone().concat(encrypted.ciphertext); // WordArray instance
var ct = ct.toString(CryptoJS.enc.Base64); // string instance
console.log(ct);

// example data
var iv = "0102030405060708090a0b0c0d0e0f";
var text = "text";
var key = "1112131415161718191a1b1c1d1e1f";

// actual code
var ivWords = CryptoJS.enc.Hex.parse(iv); // WordArray instance
var plaintext = CryptoJS.enc.Utf8.parse(text); // WordArray instance
var keyWords = CryptoJS.enc.Hex.parse(key); // WordArray instance
var encrypted = CryptoJS.AES.encrypt(plaintext, keyWords, { iv: ivWords }); // CipherParams instance

var ct = ivWords.clone().concat(encrypted.ciphertext); // WordArray instance
var ct = ct.toString(CryptoJS.enc.Base64); // string instance
output.innerHTML = ct;
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/enc-base64-min.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<div id="output"></div>

This would produce the same output as

base64_encode($iv.openssl_encrypt($data, 'aes-256-cbc', $secret, 0, $iv));

as long as iv is actually hex-encoded string with that contains the same bytes as the decoded version in $iv. The same must be true for text (except for the encoding), key and $data, $secret, respectively.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Awesome!! Is there a way to generate the iv similarly to how I generate it using php `mcrypt_create_iv()`? Because if I generate by say `var iv = ""` And I use the same `text` twice it will return the same `ct`? – maxisme Mar 10 '16 at 21:51
  • Well, you need to generate a fresh (and unpredictable) IV for each encryption. `Math.random()` is not random enough for cryptographic properties. See my answer under the following link for more information: [A: Encrypting with CryptoJS and decrypt with php: What is the use of the IV?](http://stackoverflow.com/a/31651483/1816580) – Artjom B. Mar 10 '16 at 22:06
  • Thank you for that! I am getting the error `Invalid array length` on this line: `var ct = ivWords.clone().concat(encrypted.ciphertext);` ? – maxisme Mar 10 '16 at 22:21
  • Try it without the `.clone()`. I've looked into the function and it seems to be implemented incorrectly. – Artjom B. Mar 10 '16 at 22:30
  • I can't reproduce your issue. I've added a runnable snippet. – Artjom B. Mar 10 '16 at 22:36
  • also If i run the output of your snippet with my php decode function there is no output. - `echo decrypt("AQIDBAUGBwgJCgsMDQ4P8/K4xC8WwbHJjyoSBUxshw==","1112131415161718191a1b1c1d1e1f")` – maxisme Mar 10 '16 at 23:56
  • You're not using the same key. I'm using a 32 character hex-encoded key, which is actually 16 bytes long (128 bit). It seems your PHP code isn't parsing the key as hex, but instead uses it directly. – Artjom B. Mar 11 '16 at 00:06
  • So should I remove all the `CryptoJS.enc.Hex.parse(`? – maxisme Mar 11 '16 at 00:29
  • I don't know how your key looks, but if it is randomly generated (recommended) and written without any encoding to the JavaScript block, then it is most likely a key with some bytes missing and therefore broken. – Artjom B. Mar 11 '16 at 00:43
  • ahgg I am so stressed out by this!! I do not understand why none of this is working :( – maxisme Mar 11 '16 at 00:50
  • here is the php without generating a random iv and using the same one you preset http://pastebin.com/7yS527au – maxisme Mar 11 '16 at 00:55
  • Your IV is too long. Don't confuse binary strings and encoded strings. – Artjom B. Mar 11 '16 at 01:00