Search code examples
phpnode.jscakephpencryptionnode-crypto

Encrypt with CakePHP 3.0 and decrypt with NodeJS


I have administration site developed with CakePHP 3.0 framework and i use default Security::encrypt($text, $key, $hmacSalt = null) to encrypt token for API authorization.

I also have simple NodeJS service for real time communication and i want to use the same token for API and this real time communication.

I try to rewrite CakePHP decryption function to NodeJS on different ways but i can't get correct results. Below is CakePHP decrypt function:

public static function decrypt($cipher, $key)
    {
        $method = 'AES-256-CBC';
        $ivSize = openssl_cipher_iv_length($method);

        $iv = mb_substr($cipher, 0, $ivSize, '8bit');

        echo "---- IV --- \r\n";
        var_dump($iv);

        $cipher = mb_substr($cipher, $ivSize, null, '8bit');

        echo "---- KEY --- \r\n";
        var_dump($key);

        echo "---- CIPHER LAST --- \r\n";
        var_dump($cipher);

        return openssl_decrypt($cipher, $method, $key, OPENSSL_RAW_DATA, $iv);
    }

Result from CakePHP:

---- IV --- 
string(16) "��r�N3U�Y6Q�#��"
---- KEY --- 
string(32) "1c494314996afe280bc5981c4e185f79"
---- CIPHER LAST --- 
string(160) "~a�xh�z��+���M����j*!�(����f�ZG;�)w��Kl�3�m��Z��ە��OR9~���6[X�/��n��B6��C��˟f��!6��1���|S��*�mG+���OR�kr��t�;�+�㟱��"���<i����e:��"

Here is my simple code in NodeJS:

var buf = new Buffer(socket.handshake.query.token, 'base64').toString('utf8', 64);
var iv = buf.substr(0,16);

console.log("-----IV------")
console.log(iv);

var key = sha256(config.tokenKey+config.tokenSalt).substr(0,32);
console.log("-----KEY------")
console.log(key);

var cipher = buf.substr(16);

console.log("------CIPHER-----");
console.log(cipher);

var decipher = crypto.createDecipheriv('AES-256-CBC', key, iv);
//decipher.setAutoPadding(false);
var dec = decipher.update(cipher);
dec += decipher.final('utf-8');

Result from NodeJS:

-----IV------
��r�N3U�Y6Q�#��
-----KEY------
1c494314996afe280bc5981c4e185f79
------CIPHER-----
~a�xh�z��+���M���
                 ��j*!�(����f�ZG;�)w��Kl��m���Z����ە��OR9~���6[X�/��n��B6��C��˟f���!6��1���|S��*�mG+���OR�kr��t�;�+�㟱��"���<i����e:��
crypto.js:239
  this._handle.initiv(cipher, toBuf(key), toBuf(iv));
Error: Invalid IV length

I try to create IV on different ways, but it doesnt work, even if i succeed to avoid exception i dont get correct result, I assume that issue is in "8bit" encoding in PHP code.

If someone know how to solve this i would be very thankful!


Solution

  • I'm not overly familiar with Node.js, but what I can see is that you screw up the data when you convert the input to an UTF-8 string, you need to work with binary data, not with a string.

    I'd suggest to work with buffers until you actually need to convert something to a string, at least that's how I did it when I had to decrypt data that was encrypted with CakePHP:

    var data = Buffer.from(socket.handshake.query.token, 'base64');
    var key = config.tokenKey;
    var salt = config.tokenSalt;
    
    var hmacSize = 64;
    var ivSize = 16;
    var keySize = 32;
    
    var encrypted = data.slice(hmacSize);
    
    key = crypto
        .createHash('sha256')
        .update(key + salt)
        .digest('hex')
        .substr(0, keySize);
    
    var iv = encrypted.slice(0, ivSize);
    
    encrypted = encrypted.slice(ivSize);
    
    var decipher = crypto.createDecipheriv('AES-256-CBC', key, iv);
    var decrypted = Buffer.concat([
        decipher.update(encrypted),
        decipher.final()
    ]);
    
    console.log(decrypted.toString('utf8'));