8

I'm trying to encrypt and decrypt data on the server side and the client using the same type of operation, which is AES-256.

On the server I use PHP and client I use CryptoJS so far I could only encrypt and decrypt the client on the server, see the code:

JS

<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/rollups/pbkdf2.js"></script>
<script>
    var salt = CryptoJS.lib.WordArray.random(128/8); 
    var key256Bits500Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 256/32, iterations: 500 });
    var iv  = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');

    var encrypted = CryptoJS.AES.encrypt("Message", key256Bits500Iterations, { iv: iv });  
    var data_base64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64); 
    var iv_base64   = encrypted.iv.toString(CryptoJS.enc.Base64);       
    var key_base64  = encrypted.key.toString(CryptoJS.enc.Base64);
</script>

PHP

<?php
    $encrypted = base64_decode("data_base64"); // data_base64 from JS
    $iv        = base64_decode("iv_base64");   // iv_base64 from JS
    $key       = base64_decode("key_base64");  // key_base64 from JS

    $plaintext = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv ), "\t\0 " );

How can I encrypt and decrypt data on both sides (client and server) so that communicate in the same language using PHP and CryptoJS?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
thebestclass
  • 169
  • 1
  • 3
  • 9
  • Keys must be 16, 24 or 32 bytes long and IVs must be 16 bytes long. Are they? – Artjom B. Apr 21 '15 at 14:24
  • @ArtjomB. Correct this error, but is now resulting in many strange characters (failure), which can be...? – thebestclass Apr 21 '15 at 14:54
  • You completely changed the question. Don't do that, because any answer immediately loses all value. It doesn't make sense anymore. You need to parse the key and IV. – Artjom B. Apr 21 '15 at 15:08
  • @ArtjomB. I'm sorry, your question solved my main problem and will be marked as correct .. But another problem appeared .. you know how the cryptojs wraps the encryption output? I think this is the problem – thebestclass Apr 21 '15 at 15:22
  • See my updated answer. I added the full code there without PBKDF2. I also rolled back your question, so that my answer makes sense again. I hope you see the problem. – Artjom B. Apr 21 '15 at 15:33

1 Answers1

6

Your code looks fine apart from a padding mismatch. CryptoJS uses PKCS#5/PKCS#7 padding by default whereas MCrypt only supports ZeroPadding.

If you're only sending textual plaintexts, then you can safely use

CryptoJS.AES.encrypt("Message", key, { iv: iv, padding: CryptoJS.pad.ZeroPadding });

If not, then you should use proper pkcs7unpad in PHP:

$plaintext = pkcs7unpad( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv ), 16 );

Other problems with your code are that you directly use CryptoJS.AES.encrypt(...).toString(). This will create an OpenSSL formatted string which is not purely the ciphertext. You need to use

CryptoJS.AES.encrypt(...).ciphertext.toString(CryptoJS.enc.Base64);

to also be sure about the encoding.


Right now, this is only obfuscation, since you're sending the key along with the ciphertext. I suspect that you want to derive the key in PHP too. If yes, then you will only need to send the random salt along with the ciphertext under the assumption that the server knows the password.

PHP provides a PBKDF2 implementation from version 5.5 onwards.


Full JavaScript part without PBKDF2 involvement:

var message = 'My string - Could also be an JS array/object';
var iv = 'a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8';
var key = 'c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8'; // 256-bit hex encoded

var keyBytes = CryptoJS.enc.Hex.parse(key);
var ivBytes = CryptoJS.enc.Hex.parse(iv);

var encrypt = CryptoJS.AES.encrypt(message, keyBytes, {
    iv: ivBytes, 
    padding: CryptoJS.pad.ZeroPadding 
}).ciphertext.toString(CryptoJS.enc.Base64);

produces:

j86KHBVRsDGKUnOiYdkEotsFL/lY/1tzz/h3Ay+vlEX11fC055m7vaF6q7w13eUj

Full PHP part without PBKDF2 involvement:

<?php

$iv = 'a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8';
$key = 'c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8';
$ct = 'j86KHBVRsDGKUnOiYdkEotsFL/lY/1tzz/h3Ay+vlEX11fC055m7vaF6q7w13eUj';

$ivBytes = hex2bin($iv);
$keyBytes = hex2bin($key);
$ctBytes = base64_decode($ct);

$decrypt = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $keyBytes, $ctBytes, MCRYPT_MODE_CBC, $ivBytes));

echo $decrypt;

produces:

My string - Could also be an JS array/object

The same is possible with the OpenSSL extension:

<?php
$iv = 'a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8';
$key = 'c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8';
$ct = 'j86KHBVRsDGKUnOiYdkEotsFL/lY/1tzz/h3Ay+vlEX11fC055m7vaF6q7w13eUj';

$ivBytes = hex2bin($iv);
$keyBytes = hex2bin($key);
$ctBytes = base64_decode($ct);

$decrypt = openssl_decrypt($ctBytes, "aes-256-cbc", $keyBytes, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $ivBytes);
echo($decrypt);
Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Great !! Thank you !!! could develop with your help, I am very happy =DDD. I just left a doubt that is linked to the question of context: How still decrypt in the JS? I tried this code but still will not .. `var decrypt = CryptoJS.AES.encrypt(encrypt, keyBytes, { iv: ivBytes});` – thebestclass Apr 21 '15 at 15:57
  • In that case, you need to create a `CipherParams` object and pass it to the decryption function: `var decrypt = CryptoJS.AES.decrypt(CryptoJS.lib.CipherParams({ciphertext: CryptoJS.enc.Base64.parse(encrypt)}), keyBytes, { iv: ivBytes});`. See: https://code.google.com/p/crypto-js/#The_Cipher_Input – Artjom B. Apr 21 '15 at 16:06
  • I read what you asked me, but returns me this error: **CryptoJS.lib.CipherParams is not a function** You know what can it be? I have included all the necessary files .. (forgive my ignorance) – thebestclass Apr 21 '15 at 16:30
  • Sorry, it's `CryptoJS.lib.CipherParams.create({...})` not `CryptoJS.lib.CipherParams({...})`: https://code.google.com/p/crypto-js/source/browse/tags/3.1.2/src/cipher-core.js#511 – Artjom B. Apr 21 '15 at 16:35
  • hey, this has resulted in an array of words, and then I translate the using `toString()` but resulted in a numerical code that does not correspond with the message (I'm using your example) – thebestclass Apr 21 '15 at 16:46
  • 1
    Yes, it's a bit hard to do this on the fly. Here's a fiddle that does this: http://jsfiddle.net/artjomb/rsLjhcxk/ – Artjom B. Apr 21 '15 at 16:55
  • Sorry about that, but how to generate KEYS and IV from normal strings? @ArtjomB. – thebestclass Apr 22 '15 at 19:14
  • Splendid! Still works to this day (crypto-js part) – Marco Dec 03 '21 at 15:05