Search code examples
node.jsencryptionaes

matching an encryption method in NodeJS compatible with the decryption one


I have a method in NodeJS that decrypt a payload coming form a C# request and it is working fine.

function _decryptMessage(_encryptedData, _encryptedDek, _iv, _privateKey, _kek){

    const decrypted = crypto.privateDecrypt({
      key: _privateKey,
      passphrase: _kek,
      padding: crypto.constants.RSA_PKCS1_PADDING
    }, Buffer.from(_encryptedDek, "base64"));

    const dek = decrypted.toString("utf16le");
    const buff = Buffer.from(_encryptedData, "base64");
    const key = Buffer.from(dek, "utf8");
    const iv = Buffer.from(_iv, "utf8");
    const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);

    return decipher.update(buff).toString() + decipher.final().toString();
 }

For unit testing reason, I would like to write a NodeJS equivalent method that encrypts a json payload that can be decrypted by the above function. I am trying to revert the code but something is missing because I am getting a error:0200009F:rsa routines::pkcs decoding error

This is the unfinshed method I cannot complete:

function _encrypt(_publicKey, _data){

    const dek = "bf3c199c2470cb477d907b1e0917c17b";
    const iv = "5183666c72eec9e4";

    const encryptedData= (() => {

      const cipher = crypto.createCipheriv("aes-256-cbc", dek, iv);
      let encryptedData = cipher.update(_data, 'utf8', 'base64');
      encryptedData += cipher.final('base64');

      return {
        encryptedData
      };

    })();

    const encryptedDek = crypto.publicEncrypt({
      key: _publicKey,
      oaepHash: "sha1",
      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
    }, dek).toString("base64");
    return {
      encryptedDek,
      iv,
      encryptedData
    };

 }

I would need help to complete the encrypt method that can use the above decrypt as it is.


Solution

  • In the _encrypt() function:

    • the AES key for the RSA encryption must be encoded with UTF-16LE
    • PKCS#1 v1.5 must be applied (and not OAEP)
    • the encryptedData must be returned as string (and not as object)
    function _encrypt(_publicKey, _data){
    
        const dek = "bf3c199c2470cb477d907b1e0917c17b";
        const iv = "5183666c72eec9e4";
    
        const encryptedData= (() => {
            const cipher = crypto.createCipheriv("aes-256-cbc", dek, iv);
            let encryptedData = cipher.update(_data, 'utf8', 'base64');
            encryptedData += cipher.final('base64');
            return encryptedData; // Fix 3
        })();
    
        const encryptedDek = crypto.publicEncrypt({
            key: _publicKey,
            padding: crypto.constants.RSA_PKCS1_PADDING // Fix 2
        }, Buffer.from(dek, 'utf16le')).toString("base64"); // Fix 1
        
        return {
            encryptedDek,
            iv,
            encryptedData
        };
    }
    

    With this change, a ciphertext generated with _encrypt() can be decrypted with _decryptMessage().


    Note that the derivation of key and IV from a string using UTF-8 encoding is a vulnerability that is forced on the encryption method by the design of the decryption method.

    In addition, the key and IV are usually not hardcoded, but randomly generated for each encryption (but probably the hardcoded values are due to your test case).