通过 CryptoJS 构建解密函数

Building a decryption function by CryptoJS

我在使用 CryptoJS 构建解密函数时遇到问题。

我已经通过函数 create_mpg_aes_encrypt() 创建了一个加密字符串,并尝试构建另一个函数 create_mpg_aes_decrypt() 来解密我生成的字符串。

但是,我得不到我想要的结果。

另一方面,我构建了一个函数 create_aes_encrypt_try(),来测试 CryptoJS 如何进行解密。我成功的得到了我想要的结果。 但是,根据我的观察,我传递给 CryptoJS.AES.decrypt() 的参数不是一个字符串,而是一个数组。

我要构建的函数是解密字符串。有什么方法可以解析字符串,或者我该如何构建这个函数?

  //------------------Info--------------------------------------------------------------
let parameter = {
    MerchantID: "3430112",
    RespondType: "JSON",
    TimeStamp: "1485232229",
    Version: "1.4",
    MerchantOrderNo: "S_1485232229",
    Amt: 40,
    ItemDesc: "UnitTest",
}

//-------------------Encryption-------------------------------------------------------
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";

const CryptoJS = require('crypto-js');
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 str = create_mpg_aes_encrypt(parameter, key, encrypt_mode);
//console.log(str);

let try_decryption = create_mpg_aes_encrypt_try(parameter, key, encrypt_mode);

let result = create_aes_decrypt(str, key, encrypt_mode);
console.log(result);
//-----------------------------Function-----------------------------------------------
function create_mpg_aes_encrypt(parameter, key, encrypt_mode){
    //URL ENCODED QUERY STRING
    let params = new URLSearchParams(parameter);
    let str = params.toString();
    str = addpadding(str);
    console.log(str, "\n");
    
    let encrypted_data = CryptoJS.AES.encrypt(str, key, encrypt_mode);
    
    encrypted_data = encrypted_data.ciphertext.toString(CryptoJS.enc.Hex);
    
    str = encrypted_data;
    str = str.trim();
    return str;
}

function hex2bin (s) {
  const ret = []
  let i = 0
  let l
  s += ''
  for (l = s.length; i < l; i += 2) {
    const c = parseInt(s.substr(i, 1), 16)
    const k = parseInt(s.substr(i + 1, 1), 16)
    if (isNaN(c) || isNaN(k)) return false
    ret.push((c << 4) | k)
  }
  return String.fromCharCode.apply(String, ret)
}

function addpadding(str){
    const blocksize = 32;
    let len = str.length;
    let pad = blocksize - (len % blocksize);
    let string = str;
    for(let i = 0; i < pad; i++) {
        string += String.fromCharCode(pad);
    }
    return string;
}

function create_aes_decrypt(tradeinfo, key, encrypt_mode) {
    let return_str = "";
    let decrypted_data = CryptoJS.AES.decrypt(tradeinfo, key, encrypt_mode);
    
    decrypted_data = decrypted_data.toString();
    //console.log(decrypted_data, "\n");
    
    return_str = decrypted_data;
    return return_str;
}

function create_mpg_aes_encrypt_try(parameter, key, encrypt_mode){
    // URL ENCODED QUERY STRING
    let params = new URLSearchParams(parameter);
    let str = params.toString();
    str = addpadding(str);
    
    let encrypted_data = CryptoJS.AES.encrypt(str, key, encrypt_mode);
    let decrypted_data = CryptoJS.AES.decrypt(encrypted_data, key, encrypt_mode);
    
    encrypted_data = encrypted_data.ciphertext.toString(CryptoJS.enc.Hex);
    decrypted_data = decrypted_data.toString();
    decrypted_data = hex2bin(decrypted_data);
    
    console.log(encrypted_data, "\n");
    console.log(decrypted_data, "\n");
    
    str = encrypted_data;
    str = str.trim();
    return str;
}

    // The result I want
    /*the string going to be encrypted
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest*/
    
    /*the string after encryption
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa*/
    /* After Decryption
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest*/

以下是我想实现的php参考代码

function create_aes_decrypt($parameter = "", $key = "", $iv = "") {
return openssl_decrypt(hex2bin($parameter),'AES-256-CBC', 
$key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); 

}

解密失败,因为 CryptoJS.AES.decrypt() 需要一个 CipherParams 对象:

{ciphertext: CryptoJS.enc.Hex.parse(ciphertextHex)}

其中 ciphertextHex 是十六进制编码的密文。如果你修复了这个问题,解密就可以了。这显示在下面的 JavaScript 中。

  //------------------Info--------------------------------------------------------------
let parameter = {
    MerchantID: "3430112",
    RespondType: "JSON",
    TimeStamp: "1485232229",
    Version: "1.4",
    MerchantOrderNo: "S_1485232229",
    Amt: 40,
    ItemDesc: "UnitTest",
}

//-------------------Encryption-------------------------------------------------------
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";

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 str = create_mpg_aes_encrypt(parameter, key, encrypt_mode);

let try_decryption = create_mpg_aes_encrypt_try(parameter, key, encrypt_mode);

let result = create_aes_decrypt(str, key, encrypt_mode);
document.getElementById("pt_3").innerHTML = result;
//-----------------------------Function-----------------------------------------------
function create_mpg_aes_encrypt(parameter, key, encrypt_mode){
    //URL ENCODED QUERY STRING
    let params = new URLSearchParams(parameter);
    let str = params.toString();
    str = addpadding(str);
    document.getElementById("pt_1").innerHTML = str;
    
    let encrypted_data = CryptoJS.AES.encrypt(str, key, encrypt_mode);
    
    encrypted_data = encrypted_data.ciphertext.toString(CryptoJS.enc.Hex);
    
    str = encrypted_data;
    str = str.trim();
    return str;
}

function hex2bin (s) {
  const ret = []
  let i = 0
  let l
  s += ''
  for (l = s.length; i < l; i += 2) {
    const c = parseInt(s.substr(i, 1), 16)
    const k = parseInt(s.substr(i + 1, 1), 16)
    if (isNaN(c) || isNaN(k)) return false
    ret.push((c << 4) | k)
  }
  return String.fromCharCode.apply(String, ret)
}

function addpadding(str){
    const blocksize = 32;
    let len = str.length;
    let pad = blocksize - (len % blocksize);
    let string = str;
    for(let i = 0; i < pad; i++) {
        string += String.fromCharCode(pad);
    }
    return string;
}

function create_aes_decrypt(tradeinfo, key, encrypt_mode) {
    let return_str = "";
    
    /*
    let decrypted_data = CryptoJS.AES.decrypt(tradeinfo, key, encrypt_mode);        
    decrypted_data = decrypted_data.toString();
    */
    // Fix
    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 = decrypted_data;
    return return_str;
}

function create_mpg_aes_encrypt_try(parameter, key, encrypt_mode){
    // URL ENCODED QUERY STRING
    let params = new URLSearchParams(parameter);
    let str = params.toString();
    str = addpadding(str);
    
    let encrypted_data = CryptoJS.AES.encrypt(str, key, encrypt_mode);
    let decrypted_data = CryptoJS.AES.decrypt(encrypted_data, key, encrypt_mode);
    
    encrypted_data = encrypted_data.ciphertext.toString(CryptoJS.enc.Hex);
    decrypted_data = decrypted_data.toString();
    decrypted_data = hex2bin(decrypted_data);
    
    document.getElementById("ct_2").innerHTML = encrypted_data;
    document.getElementById("pt_2").innerHTML = decrypted_data;
   
    str = encrypted_data;
    str = str.trim();
    return str;
}

// The result I want
/*the string going to be encrypted
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest*/
    
/*the string after encryption
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa*/
/* After Decryption
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="pt_1"></p>
<p style="font-family:'Courier New', monospace;" id="ct_2"></p>
<p style="font-family:'Courier New', monospace;" id="pt_2"></p>
<p style="font-family:'Courier New', monospace;" id="pt_3"></p>


目前,当使用 CryptoJS 代码加密时,您在 32 字节的块大小上使用 PKCS7 填充,这对于块大小为 16 字节的 AES 来说效率低下。
当使用 PHP 代码(因此也使用 CryptoJS 代码)解密时,填充被禁用,即未被删除,这对于 PKCS7 来说是相当不寻常的。
因此不清楚 CryptoJS 代码中是否使用了正确的填充进行加密。因此,如果 CryptoJS 代码中使用的填充确实与 PHP 代码中使用的填充匹配,则应检查加密。