Home > Net >  Convert encryption/decryption function from PHP to NodeJS
Convert encryption/decryption function from PHP to NodeJS

Time:12-17

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.

  • Related