I am looking at porting a project from PHP to NodeJS and within it contains an encryption/decryption class to encrypt and decrypt strings and I am trying to convert it to NodeJS.
Below is the existing PHP function
public function encrypt($data): string
{
return base64_encode(openssl_encrypt($data, 'AES-256-CBC', $this->cipher_key,
OPENSSL_RAW_DATA, $this->iv));
}
public function decrypt($encryptedString)
{
try
{
return $this->strippadding(openssl_decrypt(base64_decode($encryptedString), 'AES-256-CBC',
$this->cipher_key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv));
}
catch (\Exception $e)
{
Logger::log("Failed to decrypt string. Error: " . $e->getMessage());
return $encryptedString;
}
}
private function strippadding($string)
{
error_reporting(0);
$slast = ord(substr($string, -1));
$slastc = chr($slast);
//$pcheck = substr($string, -$slast);
if(preg_match('/'.$slastc.'{'.$slast.'}/', $string)){
return substr($string, 0, strlen($string)-$slast);
} else {
return null;
}
}
And below is my ported code to NodeJS
const encrypt = (data, key, iv) => {
const cipher = crypto.createCipheriv('AES-256-CBC', key, iv);
let encrypted = cipher.update(data, 'utf8', 'base64');
encrypted = cipher.final('base64');
return encrypted;
}
const decrypt = (encryptedString, key, iv) => {
try {
const decipher = crypto.createDecipheriv('AES-256-CBC', key, iv);
decipher.setAutoPadding(false);
let decrypted = decipher.update(encryptedString, 'base64', 'utf-8');
decrypted = decipher.final('utf-8');
return strippadding(decrypted);
return decrypted
} catch (e) {
console.log(`Failed to decrypt string. Error: ${e.message}`);
return encryptedString;
}
}
const strippadding = (string) => {
const slast = string.charCodeAt(string.length - 1);
const slastc = String.fromCharCode(slast);
const regex = new RegExp(`${slastc}{${slast}}`);
if (regex.test(string)) {
return string.substring(0, string.length - slast);
} else {
return null;
}
}
When I try and take an existing encrypted string and use the same key and iv to decrypt it I get a NULL returned as the regex in the strippadding function doesn't seem to work but I can't see why. If I don't use the strippadding and just print the decrypted variable I just get random symbols.
If I try and encrypt a string I then get completely different string to what I'm expecting so I have something not quite right but not sure what.
CodePudding user response:
Since according to your comment the implementation should only decrypt the ciphertexts that have been produced with the encrypt()
method and the encrypt()
method applies the default PKCS#7 padding, the default PKCS#7 padding can also be used for decryption, i.e. the custom unpadding strippadding()
is no longer needed. A possible NodeJS implementation is:
var crypto = require('crypto');
const encrypt = (data, key, iv) => {
const cipher = crypto.createCipheriv('AES-256-CBC', key, iv);
let encrypted = cipher.update(data, 'utf8', 'base64');
encrypted = cipher.final('base64');
return encrypted;
}
const decrypt = (encryptedString, key, iv) => {
try {
const decipher = crypto.createDecipheriv('AES-256-CBC', key, iv);
let decrypted = decipher.update(encryptedString, 'base64', 'utf-8');
decrypted = decipher.final('utf-8');
return decrypted
} catch (e) {
console.log(`Failed to decrypt string. Error: ${e.message}`);
return encryptedString;
}
}
iv = '0123456789012345';
key = '01234567890123456789012345678901';
plaintext = 'The quick brown fox jumps over the lazy dog';
ciphertext = encrypt(plaintext, key, iv);
console.log(ciphertext); // 4G9jpxHot6qflEAQfUaAoReZQ4DqMdKimblTAtQ5uXAsjmWpkjbskgcEkVzxqYpE
decrypted = decrypt(ciphertext, key, iv);
console.log(decrypted); // The quick brown fox jumps over the lazy dog
Test: The following PHP code uses the posted method enrypt()
and a tailored decrypt()
:
class Test {
private $iv = '0123456789012345';
private $cipher_key = '01234567890123456789012345678901';
public function encrypt($data)
{
return base64_encode(openssl_encrypt($data, 'AES-256-CBC', $this->cipher_key, OPENSSL_RAW_DATA, $this->iv));
}
public function decrypt($data)
{
try
{
return openssl_decrypt(base64_decode($data), 'AES-256-CBC', $this->cipher_key, OPENSSL_RAW_DATA, $this->iv);
}
catch (\Exception $e)
{
Logger::log("Failed to decrypt string. Error: " . $e->getMessage());
return $encryptedString;
}
}
}
$test = new Test();
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ciphertext = $test->encrypt($plaintext);
print($ciphertext . PHP_EOL); // 4G9jpxHot6qflEAQfUaAoReZQ4DqMdKimblTAtQ5uXAsjmWpkjbskgcEkVzxqYpE
$decrypted = $test->decrypt($ciphertext);
print($decrypted . PHP_EOL); // The quick brown fox jumps over the lazy dog
For the same key and plaintext, the ciphertext is the same as the NodeJS code, analogously for the decrypted text.