Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AES 256 on the client side (JS) and in the server (PHP)

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?

like image 894
thebestclass Avatar asked Apr 21 '15 00:04

thebestclass


People also ask

What is AES encryption in PHP?

AES stands for Advanced Encryption Standard. AES encryption has three block ciphers which are AES-128 (128 bit), AES-192 (192 bit), AES-256 (256 bit). These block ciphers are named due to the key used for the encryption and decryption process.

Is AES 256 still secure in 2021?

256-bit encryption is a security protocol that encrypts and decrypts the data exchanged between the browser and the server using the 256-bit encryption key. This is the safest digital security solution and hence all the modern algorithms, AES, as well as SSL (Secure Socket Layers), certificates use 256-bit encryption.

Is AES 256 better than AES-128?

Picking Between AES-128 and AES-256AES-128 is faster and more efficient and less likely to have a full attack developed against it (due to a stronger key schedule). AES-256 is more resistant to brute force attacks and is only weak against related key attacks (which should never happen anyway).


1 Answers

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);
like image 51
Artjom B. Avatar answered Nov 14 '22 22:11

Artjom B.