Node.js中的crypto模块与AES256解密中的CryptoJS的区别

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

我目前正在尝试将使用 CryptoJS 的解密代码转换为使用 node.js 中的加密模块的方法。 我猜他们的填充设置有一些差异导致我的代码出错。

以下部分是我使用加密模块的代码:

// 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;
}

这是我在加密模块代码中收到的错误消息:Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

下面的部分是使用CryptoJS的代码,我可以成功得到我预期的结果。

// 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;
}

导致此问题的原因是加密模块和 CryptoJS 之间的填充或编码差异吗?

下面的部分是我期望得到的结果。

{"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"}}

由于您使用的是自定义填充,因此您需要禁用默认的 PKCS#7 填充:

decipher.setAutoPadding(false);

此调用必须在 update()final() 调用之前完成。