I am sending data through insecure connection between Apache and Node.js servers. I need to encrypt data in PHP and decrypt in Node.js. I've spent 2 days trying to get it to work, however I only managed to get message signing to work, no encryption. I tried passing AES128-CBC, AES256-CBC, DES, AES128, AES256 as algorithms, however nothing worked well..
I tried this in PHP:
$data = json_encode(Array('mk' => $_SESSION['key'], 'algorithm' => 'SHA1', 'username' => $_SESSION['userid'], 'expires' => $expires));
$payload = openssl_encrypt($data, 'des', '716c26ef');
return base64_encode($payload);
And in Node.js:
var enc_json = new Buffer(response[1], 'base64');
var decipher = crypto.createDecipher('des', '716c26ef');
var json = decipher.update(enc_json).toString('ascii');
json += decipher.final('ascii');
And besides wrong decrypted data I get error such as these:
TypeError: error:0606508A:digital envelope routines:EVP_DecryptFinal_ex:data not multiple of block length
TypeError: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
I need a simple encryption as data is not too sensitive (no password or user data), however data should only be read by the recipient. Key length can be anything, but procedure to encrypt/decrypt has to be as simple as possible, please no IVs.
I was struggling with the same problem this week but in the opposite way (PHP encrypts -> NodeJS decrypts) and had managed to get this snippet working:
aes256cbc.js
aes256cbc.php
The secret here to avoid falling in key/iv size/decryption problems is to have the secret of exactly 32 characters length and 16 for the IV. Also, it is VERY important to use 'base64' and 'utf8' in NodeJS since these are the defaults in PHP.
Here are some sample runs:
NOTE:
I use a "timestamp|message" format to avoid man in the middle attacks. For example, if the encrypted message contains an ID to be authenticated, the MitM could capture the message and re-send it every time he wants to re-authenticate.
Therefore, I could check the timestamp on the encrypted message to be within a little time interval. This way, the same message is encrypted differently each second because of the timestamp, and could not be used out of this fixed time interval.
EDIT:
Here I was misusing the Initialization Vector (IV). As @ArtjomB. explained, the IV should be the first part of the encrypted message, and also it should be a random value. It's also recommended to use a
hmac
value in a HTTP Header (x-hmac: *value*
) in order to validate that the message was originated from a valid source (but this does not address the "re-send" message issue previously described).Here's the improved version, including the
hmac
for php and node and the IV as a part of the encrypted message:aes256cbc.js (v2)
Note that
crypto.createHmac(...).digest('hex')
is digested withhex
. This is the default in PHP forhmac
.aes256cbc.php (v2)
Here are some sample runs:
Last but not least, if you don't have openssl mod installed in php, you can use
mcrypt
instead withrijndael128
andpkcs7
padding (source) like this:aes256cbc-mcrypt.php (v2)
Ofcourse, some tests next: