Search code examples
javascriptnode.jsencryptioncryptographycryptojs

The difference between crypto module in Node.js and CryptoJS in AES256 decryption


I'm currently trying to convert the decryption code using CryptoJS into the method using crypto module in node.js. I guess there are some difference in their padding setting that caused the error in my code.

The following part is the code I used crypto module:

// Decrypt Info
let data = "27e25136f89a4d75f41a2448b1dc4fbea38784d4087b22e1ee271872ae214c684c058746c336cd43fd2d65d7df12efa742567943d8d2e51301c8bfb04d070e8888198027c463c6beec419ad06aeeeeedbd49ce33902994e3218e2b9d1b15b34eb7111f803b110361172a5403fcb7fc98cec59be0fb13d74f10bc87d7a72cd29ada7853ac2208c0c5cff840b37fffd91d82e1d7dd9ed8f73735dc47a9bbfd3b95ef2d1f52fb77168baa7d80f25126b4cda163b1323f8070cad860bf1e1bf22810654d2a0d2962fcdd5fa4106ebc9b09a3cbc5488bea145fbb363f8fd44638716c1632a9885efd57dc344fa3e1e1a26fc0133ebb84b49628bfc210197b545d4922ebdbdeded1c64718351499486ea2a0a26bd2cf8e11c6cdea92878c91b8d4f38669ff2255b1d6f7a292f2b5a72fd4bc6e7acc582896407acc77e738c316979c350e2e7d5c20bc7e5924cd9fbec9259c2b58da04900ed7d2ffe71e6807aa789e20156110eac470bfc46a91de4b6507d728ca7fb1addf3abe22b6a71e95aca90ca710788676f36774744cac19033625d427b81fc64b80c49b9f16207c550736a04a2d0aef6a76aaeeeb1b0168db31c51ad3e62bfdcd0640e40346135e50aa68c113c03dc4a41d9688e3220a4b89bdf67b8a710dbe00b62321ac98f865d59e77f41d";
let tradeinfo = data; // Get result from Bubble
const Hashkey = "9P32kffvsqoraSlBhyluMN9U90DvXp9e";
const HashIV = "CItKoTyaRbHhSAEP";

//-------------------Encryption------------------------------------
var crypto = require('crypto');
const algorithm = "aes-256-cbc";

//-------------------Main------------------------------------------
let f_result = create_aes_decrypt(tradeinfo, Hashkey, HashIV, algorithm);
console.log(f_result);

//-------------------Function--------------------------------------
function create_aes_decrypt(tradeinfo, key, iv, algorithm) {
    let return_str = "";
    
    const decipher = crypto.createDecipheriv(algorithm, key, iv);
    let decrypted_data = decipher.update(tradeinfo, "hex", "utf-8");
    
    decrypted_data += decipher.final("utf-8");
   
    return_str = strippadding(decrypted_data);

    return return_str;
}

function strippadding(str) {
    let len = str.length
    let slast = my_substr_short(str, -1);   // use my_substr_short to replace substr in php 
    slast = ord(slast);
    slastc = String.fromCharCode(slast);

    let re = slastc + "{" + slast + "}";
    
    //console.log(my_preg_match(re, str));
    if(my_preg_match(re, str)){
        str = my_substr_full(str, 0, len - slast);
        return str;
    }
    else
        return false;
}

function my_substr_short(str, len2get) {
    let len = str.length;
    let return_str = "";
    
    if(len2get >= 0) {
        for(let i = 0; i < len2get; i++)
            return_str += str[i];
    }
    else {
        len2get = -len2get;
        if (len2get > len)
            len2get = len;
        for(let i = 0; i < len2get; i++)
            return_str += str[(len - len2get) + i];
    }
    return return_str;
}

function my_substr_full(str, start, len2get) {
    let len = str.length;
    let return_str = "";
    
    for(let i = start; i < len2get; i++)
        return_str += str[i];
    return return_str;
}   

function ord(str){
    return str.charCodeAt(0);
}

function my_preg_match(pattern, str) {
    if(str.match(pattern) != null)
        return true;
    else
        return false;
}

Here is the error message I got in my crypto module's code: Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

The following part is the code using CryptoJS, which I can successfully get the result I expected.

// Decrypt Info
let data = "27e25136f89a4d75f41a2448b1dc4fbea38784d4087b22e1ee271872ae214c684c058746c336cd43fd2d65d7df12efa742567943d8d2e51301c8bfb04d070e8888198027c463c6beec419ad06aeeeeedbd49ce33902994e3218e2b9d1b15b34eb7111f803b110361172a5403fcb7fc98cec59be0fb13d74f10bc87d7a72cd29ada7853ac2208c0c5cff840b37fffd91d82e1d7dd9ed8f73735dc47a9bbfd3b95ef2d1f52fb77168baa7d80f25126b4cda163b1323f8070cad860bf1e1bf22810654d2a0d2962fcdd5fa4106ebc9b09a3cbc5488bea145fbb363f8fd44638716c1632a9885efd57dc344fa3e1e1a26fc0133ebb84b49628bfc210197b545d4922ebdbdeded1c64718351499486ea2a0a26bd2cf8e11c6cdea92878c91b8d4f38669ff2255b1d6f7a292f2b5a72fd4bc6e7acc582896407acc77e738c316979c350e2e7d5c20bc7e5924cd9fbec9259c2b58da04900ed7d2ffe71e6807aa789e20156110eac470bfc46a91de4b6507d728ca7fb1addf3abe22b6a71e95aca90ca710788676f36774744cac19033625d427b81fc64b80c49b9f16207c550736a04a2d0aef6a76aaeeeb1b0168db31c51ad3e62bfdcd0640e40346135e50aa68c113c03dc4a41d9688e3220a4b89bdf67b8a710dbe00b62321ac98f865d59e77f41d";
let tradeinfo = data; // Get result from Bubble

const CryptoJS = require('crypto-js');
const Hashkey = "9P32kffvsqoraSlBhyluMN9U90DvXp9e";
const HashIV = "CItKoTyaRbHhSAEP";

//-------------------Encryption------------------------------------
const key = CryptoJS.enc.Utf8.parse(Hashkey);
const iv = CryptoJS.enc.Utf8.parse(HashIV);
const encrypt_mode = {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.ZeroPadding
};

//-------------------Main------------------------------------------
let f_result = create_aes_decrypt(tradeinfo, key, encrypt_mode);
console.log(f_result);

//-------------------Function--------------------------------------
function create_aes_decrypt(tradeinfo, key, encrypt_mode) {
    let return_str = "";
    let decrypted_data = CryptoJS.AES.decrypt({ciphertext: CryptoJS.enc.Hex.parse(tradeinfo)}, key, encrypt_mode);
    decrypted_data = decrypted_data.toString(CryptoJS.enc.Utf8);
    
    return_str = strippadding(decrypted_data);
    
    return return_str;
}

function strippadding(str) {
    let len = str.length
    let slast = my_substr_short(str, -1);   // use my_substr_short to replace substr in php 
    slast = ord(slast);
    slastc = String.fromCharCode(slast);

    let re = slastc + "{" + slast + "}";
    
    //console.log(my_preg_match(re, str));
    if(my_preg_match(re, str)){
        str = my_substr_full(str, 0, len - slast);
        return str;
    }
    else
        return false;
}

function my_substr_short(str, len2get) {
    let len = str.length;
    let return_str = "";
    
    if(len2get >= 0) {
        for(let i = 0; i < len2get; i++)
            return_str += str[i];
    }
    else {
        len2get = -len2get;
        if (len2get > len)
            len2get = len;
        for(let i = 0; i < len2get; i++)
            return_str += str[(len - len2get) + i];
    }
    return return_str;
}

function my_substr_full(str, start, len2get) {
    let len = str.length;
    let return_str = "";
    
    for(let i = start; i < len2get; i++)
        return_str += str[i];
    return return_str;
}   

function ord(str){
    return str.charCodeAt(0);
}

function my_preg_match(pattern, str) {
    if(str.match(pattern) != null)
        return true;
    else
        return false;
}

Is the reason causing this problem due to padding or encoding difference between crypto module and CryptoJS?

The following part is the result I expected to get.

{"Status":"SUCCESS","Message":"\u6388\u6b0a\u6210\u529f","Result":{"MerchantID":"MS121595179","Amt":400,"TradeNo":"21082522362641751","MerchantOrderNo":"1629902168","RespondType":"JSON","IP":"140.114.214.77","EscrowBank":"HNCB","PaymentType":"CREDIT","RespondCode":"00","Auth":"607048","Card6No":"400022","Card4No":"1111","Exp":"2606","TokenUseStatus":"0","InstFirst":0,"InstEach":0,"Inst":0,"ECI":"","PayTime":"2021-08-25 22:36:26","PaymentMethod":"CREDIT"}}

Solution

  • Since you are using a custom padding, you need to disable the default PKCS#7 padding with:

    decipher.setAutoPadding(false);
    

    This call must be done before the update() and final() calls.