1

I have a encryption in PHP and a decryption in node JS. I know there are many topics about this. But all of them state that it doesn't work. In my case it does. Well sort of.

PHP

define('CRYPT_KEY', 'bwAZA48aaYd34PGhBWSutIIFaCu4oAe/Ps24/Rx+bw0=');

class Encryption {
  public function encrypt($data) {
    $output = false;
    $methode = "AES-256-CBC";
    $key = base64_decode(CRYPT_KEY);

    $ivSize  = openssl_cipher_iv_length($methode);
    $ivData  = openssl_random_pseudo_bytes($ivSize);
    $encData = openssl_encrypt($data, $methode, $key, OPENSSL_RAW_DATA, $ivData);
    $output = base64_encode($ivData . $encData);

    return $output;
  }

  public function decrypt($data) {
    $output = false;
    $methode = "AES-256-CBC";
    $key = base64_decode(CRYPT_KEY);

    $ivSize  = openssl_cipher_iv_length($methode);
    $data = base64_decode($data);
    $ivData   = substr($data, 0, $ivSize);
    $encData = substr($data, $ivSize);
    $output = openssl_decrypt($encData, $methode, $key, OPENSSL_RAW_DATA, $ivData);

    return $output;
  }
}

echo $encryption->encrypt('Hello world'); 
// output: 8ZtGcgDeSCN8f+jrZ/W2tWL40DIncjmCwanFiNrEhyE=

node JS

const key = Buffer.from('bwAZA48aaYd34PGhBWSutIIFaCu4oAe/Ps24/Rx+bw0=', 'base64');
const iv = Buffer.from(crypto.randomBytes(16), 'base64');


function decrypt(data) {
  const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  const decripted = decipher.update(data, 'base64', 'utf8') + decipher.final('utf8');
  
  return decripted;
}

console.log('Output: ', decrypt('8ZtGcgDeSCN8f+jrZ/W2tWL40DIncjmCwanFiNrEhyE='));

the first part of the output is scrambled

result: ▬�2���◄p��r♣�'Hello world

Does someone know why this happens. Im I doing something wrong?

DataConnect
  • 129
  • 1
  • 8
  • 1
    On PHP-side you are (correctly) using the IV for decryption that was used on encryption side (by stripping off the first 16 bytes and use them). On Node.js-side you generate a new IV for decryption that has to fail for the first 16 bytes of your data as they contain the IV. Just do the same on Node.js side as you have done on PHP - strip of the first 16 bytes of the (Base64-decoded) data and use them for decryption of the rest. – Michael Fehr Oct 01 '22 at 11:44

1 Answers1

2

The reason for the problem is that in the PHP code IV and actual cipher text are concatenated, but in the NodeJS code the corresponding separation is missing. Instead, a new, random IV is used incorrectly.
However, for decryption to work, it must use the same IV as encryption. The IV must therefore be determined from the concatenated data.

The separation of IV and ciphertext can be done as follows:

const data = Buffer.from('tIF2bpl7Ro8lZbslAgBqJNqwIqn4pNWZrGkUaWcJQzo=', 'base64');
const iv = data.subarray(0, 16);
const ciphertext = data.subarray(16);

Since the ciphertext is now passed to decrypt() as buffer and not Base64 encoded, the decryption in decrypt() has to be changed accordingly:

const decripted = decipher.update(data, '', 'utf8') + decipher.final('utf8');

Please note that the posted NodeJS example does not work. It returns an EVP_DecryptFinal_ex:bad decrypt error, indicating that key and ciphertext are not related.

Topaco
  • 40,594
  • 4
  • 35
  • 62