1

I use the following functions to encrypt/decrypt with NodeJS, they work fine. But I was unable to decrypt the data with PHP to use in some part of the same project.

NodeJS:

function encrypt(text){
  var cipher = crypto.createCipher('aes-256-cbc','strong-key')
  var crypted = cipher.update(text,'utf8','hex')
  crypted += cipher.final('hex');
  return crypted;
}

function decrypt(text){
  var decipher = crypto.createDecipher('aes-256-cbc','strong-key')
  var dec = decipher.update(text,'hex','utf8')
  dec += decipher.final('utf8');
  return dec;
}

What I tried with PHP is:

openssl_decrypt('fdf32748aa4ce37fc600bbe7be14bfc7', 'AES-256-CBC', "strong-key");

But it keeps returning false/empty. I appreciate healping me to know what I am doing wrong.

Edit: For example, decrypting 28e1dfdedac467a015a9c8720d0a6451 with PHP should return "Hello World", using the same key as above.

tinyCoder
  • 350
  • 13
  • 37

1 Answers1

2

Make sure that your incoming data is the correct format (ie doesn't have any extra layers of encoding). It looks like hex but it's not what openssl_decrypt necessarily expects.

Here's a back and forth PHP example (using an IV which you should too):

$data = 'hello this is some data';
$key = 'this is a cool and secret password';
$iv = random_bytes(16);

$encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
echo 'iv (as hex): ', bin2hex($iv), PHP_EOL;
echo 'encrypted: ', $encrypted, PHP_EOL; // note this is not hex

$decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', $key, 0, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL;
$ php test.php 
iv (as hex): 02c00788438518f241cb86dc90237102
encrypted: oRZAXMjNle6hkJ9rTHTeUl5VoHQol+020Q/iFnbgbeU=
decrypted: hello this is some data

Edit, even more specific example, highlighting the importance of knowing your encodings:

// test.js
const crypto = require('crypto');

let data = 'hello this is some data';

const key = crypto.scryptSync('Password used to generate key', '', 32); // 256 / 8 = 32
const iv = crypto.randomBytes(16); // Initialization vector.

const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let crypted = cipher.update(data,'utf8','hex')
crypted += cipher.final('hex');

console.log('data: ', data);
console.log('key: ', key.toString('hex')); // key:  9266bc531befd01b6a55c232fa0efeb35625079e7024758b2e65d0dd72fe59df
console.log('crypted (as hex): ', crypted); // crypted (as hex):  b571d864da0680d77e4880d0071b49e456a1eead4b1cbfa42a9337965a466362
console.log('iv (as hex): ', iv.toString('hex')); // iv (as hex):  788ac1dcee25824b713b5201d07cc133

Here we know all our outputs are hex, so we can re-format them to binary data on the PHP side:

// test.php

$iv = hex2bin( '788ac1dcee25824b713b5201d07cc133' );
$encrypted = hex2bin( 'b571d864da0680d77e4880d0071b49e456a1eead4b1cbfa42a9337965a466362' );
$key = hex2bin('9266bc531befd01b6a55c232fa0efeb35625079e7024758b2e65d0dd72fe59df');

$decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL; // hello this is some data

Final Edit: Now with working key derivation implementation. This successfully decrypts your 'Hello world':

$password = 'strong-key';

// derive key and IV using function from SO, which implements same method node uses
$ar = deriveKeyAndIV($password, 1, 'aes-256-cbc', 'md5');
$key = $ar['key'];
$iv = $ar['iv'];

$decrypted = openssl_decrypt(hex2bin('28e1dfdedac467a015a9c8720d0a6451'), 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL; // Hello world

function deriveKeyAndIV($data,$count,$cipher,$digest) {
    $ivlen  = openssl_cipher_iv_length($cipher);
    $keylen = 32;
    $hash  = "";
    $hdata = "";
    while(strlen($hash) < $keylen+$ivlen) {
        $hdata .= $data;
        $md_buf = openssl_digest($hdata, $digest);
        //
        for ($i = 1; $i < $count; $i++) {
            $md_buf = openssl_digest ( hex2bin($md_buf),$digest);
        }
        $hdata = hex2bin($md_buf);
        $hash .= $hdata;
    }
    //
    $key = substr($hash,0,$keylen);
    $iv  = substr($hash,$keylen,$ivlen);
    //
    return array('key' => $key, 'iv' => $iv);
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Gavin
  • 2,214
  • 2
  • 18
  • 26
  • Thanks for the answer, I used the decryption function you posted to decrypt an already encrypted string with nodejs but it didn't work. Could you try to encrypt a string with the same nodejs function and decrypt with the php function? this is what i am trying to do. – tinyCoder Jul 07 '20 at 14:32
  • @tinyCoder see updated example, showing that you need to know what encodings the in/output of either side are using. – Gavin Jul 07 '20 at 14:54
  • Thank you for the great detailed answer, I will mark it as accepted. But, I already have encrypted data with the method i posted (without IV), and it's live in my app, if I am gonna change the method i will loose this data Is there anything I can do to avoid that? All the encrypted strings are numbers, only numbers (phone numbers without any symbols). – tinyCoder Jul 07 '20 at 15:06
  • Hmm it's a difficult one. The problem is that the node function you've been using doesn't actually take a 'key', it uses a given 'password' to derive its own key and iv which it then uses. The PHP side expects your key and iv to be known and incoming. Beyond my knowledge off the top of my head how to re-create the key/iv generation that the node side uses. – Gavin Jul 07 '20 at 16:03
  • Thank you, this makes me give up and lose the current data and starting over with a new method – tinyCoder Jul 07 '20 at 16:15
  • Perhaps not! user [here](https://stackoverflow.com/a/51885310/2244284) has recreated the same method the node function uses to create the key and iv. Somewhere to start. – Gavin Jul 07 '20 at 16:16
  • Oh my God! you're amazing. You made my day and saved me a lot of work. Full respect to people like you Gavin – tinyCoder Jul 07 '20 at 16:39
  • Funnily enough my last job heavily involved encrypted MSISDNs so I've more or less been exactly here before. – Gavin Jul 07 '20 at 16:43