I have little to no knowledge in encryption and I've been facing issues trying to figure out how to convert these PHP functions to work with node.js and the crypto module.
function encryptAES($str,$key) {
$iv = "PJKKIOKDOICIVSPC"
$str = pkcs5_pad($str);
$encrypted = openssl_encrypt($str, "AES-256-CBC", $key, OPENSSL_ZERO_PADDING, $iv);
$encrypted = base64_decode($encrypted);
$encrypted = unpack('C*', ($encrypted));
$encrypted = byteArray2Hex($encrypted);
$encrypted = urlencode($encrypted);
return $encrypted;
}
function pkcs5_pad ($text) {
$blocksize = openssl_cipher_iv_length("AES-256-CBC");
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
function byteArray2Hex($byteArray) {
$chars = array_map("chr", $byteArray);
$bin = join($chars);
return bin2hex($bin);
}
Any help would be much appreciated.
Actually, porting issues without target code are routinely closed on SO. But in this case, a target code would not bring any particular benefit, because the main problem here is not porting, but an unnecessary complicated PHP code, which should be simplified first. This will make it almost a one-liner, which will also significantly simplify the porting:
pkcs5_pad()
function implements PKCS#7 padding, which is supported by PHP/OpenSSL out-of-the-box. However, the OPENSSL_ZERO_PADDING
flag must be removed, as this disables padding. For completeness: PKCS#7 padding is often referred to as PKCS#5 padding in the Java world for historical reasons.OPENSSL_RAW_DATA
flag.unpack()
call and the byteArray2Hex()
method are not needed. Instead the ciphertext can be hex encoded directly with bin2hex()
.urlencode()
does not change the result. Therefore this call can be omitted.With this, your function can be simplified as follows:
function encryptAESsimple($str, $key){
$iv = "PJKKIOKDOICIVSPC";
$encrypted = openssl_encrypt($str, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
return bin2hex($encrypted);
}
and pkcs5_pad()
and byteArray2Hex()
are obsolete.
Test:
The two calls
print(encryptAES("The quick brown fox jumps over the lazy dog", "01234567890123456789012345678901") . PHP_EOL);
print(encryptAESsimple("The quick brown fox jumps over the lazy dog", "01234567890123456789012345678901") . PHP_EOL);
return
59d97c5ae90a1ccf2c1d4ac10aebd2db2d4c1ebf743bbe748cb65bc2109aae43e9d7425cbe5b4d17e0324965cfb0db68
59d97c5ae90a1ccf2c1d4ac10aebd2db2d4c1ebf743bbe748cb65bc2109aae43e9d7425cbe5b4d17e0324965cfb0db68
and thus identical ciphertexts.
This simplification also makes NodeJS porting much easier. You can find some examples in the NodeJS documentation, for example:
var crypto = require('crypto')
var plaintext = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8');
var key = Buffer.from('01234567890123456789012345678901', 'utf8'); // Note: for a hex or base64 encoded key you have to change the encoding from utf8 to hex or base64
var iv = Buffer.from('PJKKIOKDOICIVSPC', 'utf8');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
console.log(ciphertext.toString('hex')); // 59d97c5ae90a1ccf2c1d4ac10aebd2db2d4c1ebf743bbe748cb65bc2109aae43e9d7425cbe5b4d17e0324965cfb0db68
which gives the same ciphertext as the PHP code.
Security: The PHP code uses a static IV. This is insecure because it results in the reuse of key/IV pairs for a fixed key. Therefore, in practice, a random IV is generated for each encryption. This IV is not secret and is sent to the decrypting side along with the ciphertext, usually concatenated. The decrypting side separates both and performs the decryption.