Decrypt string in node.js from PHP RIJNDAEL_128 CBC - php

I'm trying to decrypt a string, previously encrypted by a third party software using PHP RIJNDAEL_128 in CBC mode, using node.js.
Here is an interactive link of the following PHP code, in a sandbox, so you can compile and see for yourself. http://sandbox.onlinephpfunctions.com/code/504a7d052c5b123fac8103a073c05c2ff5f80571
PHP source code:
<?php
class CryptClass{
private $key;
public function __construct($key){
$this->key = $key;
}
public function cryptage($message){
$key = base64_decode($this->key);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_CBC, $iv);
$ciphertext = $iv . $ciphertext;
return base64_encode($ciphertext);
}
public function decryptage($message){
$key = base64_decode($this->key);
$iv_size2 = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$ciphertext_dec = base64_decode($message);
$iv_dec = substr($ciphertext_dec, 0, $iv_size2);
$ciphertext_dec = substr($ciphertext_dec, $iv_size2);
$message_decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,$ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
return str_replace("\0", "", $message_decrypt);
}
}
// Secret key and data
define('KEY', 'azertyuiolskzif');
define('DATA', 'user#email.com|1477576941|origin.com');
// Crypt
$Crypt = new CryptClass(base64_encode(KEY));
$encodedData = base64_encode($Crypt->cryptage(DATA));
// Decrypt
$decodedData = $Crypt->decryptage(base64_decode($encodedData));
echo 'base64_encode: '.base64_encode(KEY);
echo "\nDATA: ".DATA;
echo "\nDATA length: ".strlen(DATA);
echo "\n\nencodedData: ".$encodedData;
echo "\n\ndata: ".$decodedData;
echo "\n\ndata length: ".strlen($decodedData);
echo "\n\ncrypt/decrypt match?: ".(DATA == $decodedData ? 'yes':'no');
Here is my implementation in node.js: NOT WORKING, see below for working solution
var crypto = require('crypto');
var textToEncrypt = 'user#email.com|1477576941|origin.com';
var encryptionMethod = 'AES-128-CBC';
var secret = "azertyuiolskzif";
var iv = 'aaaabbbbccccdddd';
var encrypt = function (plain_text, encryptionMethod, secret, iv) {
var encryptor = crypto.createCipheriv(encryptionMethod, secret, iv);
return encryptor.update(plain_text, 'utf8', 'base64') + encryptor.final('base64');
};
var decrypt = function (encryptedMessage, encryptionMethod, secret, iv) {
var decryptor = crypto.createDecipheriv(encryptionMethod, secret, iv);
return decryptor.update(encryptedMessage, 'base64', 'utf8') + decryptor.final('utf8');
};
var encryptedMessage = encrypt(textToEncrypt, encryptionMethod, secret, iv);
var decryptedMessage = decrypt(encryptedMessage, encryptionMethod, secret, iv);
console.log(decrypt());
console.log(encryptedMessage);
console.log(decryptedMessage);
I have tried many things and I'm getting lost here between Invalid key length and other error messages. One thing I don't quite understand is that the KEY apparently used to encrypt the data is azertyuiolskzif which is 15 chars long while most script use a required 32 chars string... Maybe PHP doesn't need a 32 char string but Node does?
Or maybe it's related to the difference between 128 and 256. Or is it due to the difference of padding between PHp and Node implementation?
I tried to follow the advices given at Encrypt string in PHP and decrypt in Node.js but even tho, I didn't succeed at encrypting my data in node yet.
Edit:
After some more digging around (and thanks for the explanations in the answers), I finally made crypt/decrypt work in Node.js. But I haven't succeeded to decrypt something crypted by PHP yet.
var crypto = require('crypto');
var AES = {};
AES.encrypt = function(dataToEncrypt, encryptionMethod, secret, iv, padding) {
var encipher = crypto.createCipheriv(encryptionMethod, secret, iv);
encipher.setAutoPadding(padding || 0); // "true" or "128" would work with aes-128-cbc
var encryptedData = encipher.update(dataToEncrypt, 'utf8', 'base64');
encryptedData += encipher.final('base64');
return encryptedData;
};
AES.decrypt = function(encryptedData, encryptionMethod, secret, iv, padding) {
var decipher = crypto.createDecipheriv(encryptionMethod, secret, iv);
decipher.setAutoPadding(padding || 0); // "true" or "128" would work with aes-128-cbc
var decoded = decipher.update(encryptedData, 'base64', 'utf8');
decoded += decipher.final('utf8');
return decoded;
};
// ----
var textToEncrypt = 'user#email.com|1477576941|origin.com';
var secret = "aaaabbbbccccdddd"; // Must be 16 chars
var iv = crypto.randomBytes(16); // Must be 16 chars
var encryptionMethod = 'AES-128-CBC';
// Testing crypt/decrypt using Node.js algorithm.
var encryptedMessage = AES.encrypt(textToEncrypt, encryptionMethod, secret, iv, 128);
var decryptedMessage = AES.decrypt(encryptedMessage, encryptionMethod, secret, iv, 128);
console.log('encryptedMessage', encryptedMessage); // Displays "GWpMWORNKkqlrHJDPuNgSmTKr1vJhaAApHP+ssK3SH5EALTkdWneUZRp9PXNpVQ2"
console.log('decryptedMessage', decryptedMessage); // Displays "user#email.com|1477576941|origin.com"
// Testing decrypt from a string generated by PHP algorithm.
// XXX Doesn't work "Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length"
// XXX Probably due to wrong padding between PHP/Node implementation?
var stringToDecode = 'RlM3Wkl3N3JRM0dnaEh4SkdoZWFDRy9mZGRoTnkxNlZUL2IvcHl4TkdzUUlRSXQwSWNwWUZ5OFpaRENZQys3S2t0bFZIUWoweUVsZGxUU21sYU9tS0E9PQ==';
console.log('stringToDecode', stringToDecode);
console.log(AES.decrypt(
stringToDecode,
encryptionMethod, secret, iv, 128
));

AES keys must be exactly one of 128, 192 or 256-bits. Some implementations will pad keys in some way but this should not be relied on. Make the key a correct size.

Related

Node.js v10.16.0 implementation of AES 256 CTR decryption using PHP

The key that I have to use for decryption is a 80 characters long string.
I can't figure out the length of iv needed for that kind of key in the Node.js implementation.
PHP snippet that I'm trying to convert to Node.js:
protected function Decryption($theData, $theKey) {
$method = 'aes-256-ctr';
$nonceSize = openssl_cipher_iv_length($method);
$nonce = mb_substr($theData, 0, $nonceSize, '8bit');
$ciphertext = mb_substr($theData, $nonceSize, null, '8bit');
$plaintext = openssl_decrypt( $ciphertext, $method, $theKey, OPENSSL_RAW_DATA, $nonce);
return json_decode($plaintext);
}
Node.js implementation, throwing Invalid IV length error:
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');
const decipher = crypto.createDecipheriv('aes-256-ctr', theKey, nonce);
let plainText = decipher.update(theData, 'base64', 'utf8');
plainText += decipher.final('utf8');
console.log(plainText);
This could be solved by using different length of key but in this case I need to use 80 characters.
EDIT
After updating to use the fix from Getting error of Invalid IV Length while using aes-256-cbc for encryption in node , I'm getting Invalid key length error with this code:
const crypto = require('crypto');
var iv = new Buffer(crypto.randomBytes(16))
var nonce = iv.toString('hex').slice(0, 16);
const decipher = crypto.createDecipheriv('aes-256-ctr', theKey, nonce);
let plainText = decipher.update(theData, 'base64', 'utf8');
plainText += decipher.final('utf8');
console.log(plainText);

Encrypt in node and decrypt in PHP 7 with openssl

I found this post how to encrypt in php and decrypt in node and it works:
Encrypt in PHP 7 decrypt in Node JS
But I have problem to do the same in oposite direction.
I tried like this:
Node:
const crypto = require('crypto');
const data = "data to encrypt";
const key = "315a5504d921f8327f73a356d2bbcbf1";
const iv = new Buffer(data.substring(0, 32), 'hex');
const cipher = crypto.createCipher('aes-256-cbc', key, iv);
let crypted = cipher.update(data, 'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
PHP:
<?php
$encryptedMessage = '3aa3fc237aaf34a26482674cfcef1210';
$encryptionMethod = 'aes-256-cbc';
$secretHash = "315a5504d921f8327f73a356d2bbcbf1";
//To Decrypt
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessage, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessage, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
echo "Decrypted: $decryptedMessage";
But not working, any idea how to make this work?
The IV should be random and the same IV needs to be used in both the encryption and decryption process.
Your initialization vector is based on the unecrypted string, which is a very bad idea as you'd be leaking part of your unecrypted data if you send the IV with the encrypted data.

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!
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'));

Crypto js encrypted string not decrypt in php

I am using following js code to encrypt string
var text = 'should be decrypted!';
var key = 'HighlySecretKeyForJsEncryption!!';
var encrypted = CryptoJS.AES.encrypt(text, key);
console.log(encrypted.toString());
output : U2FsdGVkX19vf+s6/+eB8A+3iKFCl1A0e+oe0BSbcMVGxb64FL35Q3CB/LZNu4ng
and this what I did in php to decrypt this
function decrypt($toDecrypt) {
$key = "HighlySecretKeyForJsEncryption!!";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$toDecrypt = base64_decode($toDecrypt);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}
But this is not working, it gives me garbage string.
From docs:
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the
variant by the size of the key you pass in. If you use a passphrase,
then it will generate a 256-bit key.
You probabily need to pass the constant MCRYPT_RIJNDAEL_256 when decrypting php-side
More about AES encryption / decrytption in php: https://stackoverflow.com/a/3422787/4499267

Node.js equivalent program for below PHP snippet

Can anyone suggest how to write Node.js equivalent program with crypto module for below PHP snippet
$source = ...;
$secretKey = pack('H*', "SECRET_KEY");
$decoded = base64_decode($source);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $secretKey, $decoded, MCRYPT_MODE_ECB);
This example is taken from https://www.npmjs.com/package/node-rijndael
// Import module
var rijndael = require('./examples/rijndael');
// Set key
var key = new Buffer('theonetruesecretkeytorulethemall', 'utf-8').toString('base64');
var iv = crypto.randomBytes(16).toString('base64');
// Encrypt message
var plaintext = 'hello, world!';
var ciphertext = rijndael.encrypt(plaintext, key, iv);
// 'hello, world!' encodes to
// '50yvJtooiLHUOAbniGgMHmZE18Op99Rhe+Y+G6AjPzM='
// Decrypt message
var decryptedMessage = rijndael.decrypt(ciphertext, key, iv);
// '50yvJtooiLHUOAbniGgMHmZE18Op99Rhe+Y+G6AjPzM=' decodes to
// 'hello, world!'

Categories