【问题标题】:How to decrypt AES 128-CBC by Crypto-JS?如何通过 Crypto-JS 解密 AES 128-CBC?
【发布时间】:2021-02-19 09:07:05
【问题描述】:

我有一个使用密钥解密有效负载的 PHP 代码,我正在尝试使用 crypto-js 库在 JavaScript 中编写完全相同的代码,但我得到了错误的结果。

有效载荷中的前 16 个字节 - 是向量,其余部分 - 是有用的信息。

PHP 中的工作代码 - https://ideone.com/NJXkRK

function getPayload($app_secret_key, $data) {
  // Get the encryption key (16 first bytes of the app's client_secret key)
  $encryption_key = substr($app_secret_key, 0, 16);
 
  // Decrypt payload
  $json_data = aes_128_decrypt($encryption_key, $data);
 
  // Decode json
  $json_decoded = json_decode($json_data, true);
  return $json_data;
}
 
function aes_128_decrypt($key, $data) {
  // Ecwid sends data in url-safe base64. Convert the raw data to the original base64 first
  $base64_original = str_replace(array('-', '_'), array('+', '/'), $data);
 
  // Get binary data
  $decoded = base64_decode($base64_original);
 
  // Initialization vector is the first 16 bytes of the received data
  $iv = substr($decoded, 0, 16);
 
  // The payload itself is is the rest of the received data
  $payload = substr($decoded, 16);
 
  // Decrypt raw binary payload
  $json = openssl_decrypt($payload, "aes-128-cbc", $key, OPENSSL_RAW_DATA, $iv);
  //$json = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $payload, MCRYPT_MODE_CBC, $iv); // You can use this instead of openssl_decrupt, if mcrypt is enabled in your system
 
  return $json;
}
 
// Get payload from the GET and process it
$ecwid_payload = "ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
$client_secret = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs"; 
 
$result = getPayload($client_secret, $ecwid_payload);
 
print($result);

不工作的 JS 代码

function getPayload() {
  const payload =
    "ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
  const key = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs";
  // Get the encryption key (16 first bytes of the app's client_secret key)
  const encryption_key = key.substr(0, 16);
  

  // Decrypt payload
  const base64_original = payload.replace(/-/gi, "+").replace(/_/gi, "/");
  const data = aes_128_decrypt(encryption_key, base64_original);
  console.log(data);
}

function aes_128_decrypt(password, data) {
  const decoded = atob(data);
  let iv = decoded.substr(0, 16);
  let payload = decoded.substr(16);

  iv = CryptoJS.enc.Hex.parse(iv);

  const decrypted = CryptoJS.AES.decrypt(payload, password, {
    iv: iv,
    padding: CryptoJS.pad.NoPadding
  });
  return decrypted.toString();
}


window.onload = function() {
  getPayload();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

请帮我改进这个JS代码

【问题讨论】:

    标签: javascript encryption aes cryptojs


    【解决方案1】:

    JavaScript 代码有几个问题:

    • 密钥不能作为字符串传递,而是作为WordArray 传递(否则 CryptoJS 使用密钥派生函数)。
    • IV 和密文未正确确定。
    • 密文必须作为 CipherParams 对象(或 Base64 编码字符串)传递。
    • 填充必须是 PKCS7(解密也可以使用 NoPadding,但不会删除填充字节)。
    • 明文必须经过 Utf8 解码(默认为 .toString() 十六进制编码)

    有关详细信息,请参阅 CryptoJS 文档,尤其是章节 The Cipher Input 和 The Cipher Output。

    以下 JavaScript 代码解密密文:

    function getPayload() {
        const payload = "ng7W9c9jLhkX7ATMpafNAd5Vt_skEaFAqnQaw0Ing1iwYQOwB0Q_CuCS8yQeHeorTdCpZWDTNrzhcq_umX7IaAFUPPgs0zyddY7Er1tA0aze5kWGHUV54fJHoVEJHMmVEi-G5g8ZnNopIFu0YQgQqLpCq8TP2zFJunSTA7VXHTmqHNAD2JXaUb-VylcJWzgV0vaCoGyHqaPbsNNw6HSWkAzhh8dLmsYB0uzsZ_zl3wVXubCL4p2N53PmNPBLCgoC";
        const key = "zcKf1Zt0UsO43S46Un3pxIgs91R1xMGs";
        // Get the encryption key (16 first bytes of the app's client_secret key)
        //const encryption_key = key.substr(0, 16);
        const encryption_key = CryptoJS.enc.Utf8.parse(key.substr(0, 16));              // Parse the key into a WordArray
      
        // Decrypt payload
        const base64_original = payload.replace(/-/gi, "+").replace(/_/gi, "/");
        const data = aes_128_decrypt(encryption_key, base64_original);
        console.log(data.replace(/(.{56})/g,'$1\n'));                                   // {"store_id":20553036,"access_token":"secret_a9TmTJfRt3gyvxjJ9UwYjs9VQip3F7rp","public_token":"public_QQ99gUwVGdvKuZbLLyNZzDsvXF5iF3gh","view_mode":"PAGE","lang":"ru"}
        //console.log(JSON.parse(data));                                                // Convert JSON string into JavaScript object (optional)
    }
    
    function aes_128_decrypt(password, data) {
        /*
        const decoded = atob(data);
        let iv = decoded.substr(0, 16);
        let payload = decoded.substr(16);
        iv = CryptoJS.enc.Hex.parse(iv);  
        */
        var ivCiphertext = CryptoJS.enc.Base64.parse(data);                             // Parse data into a WordArray
        var iv = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(0, 16 / 4));    // Separate iv
        var payload = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(16 / 4));  //    and ciphertext
    
        //const decrypted = CryptoJS.AES.decrypt(payload, password, {               
        const decrypted = CryptoJS.AES.decrypt(
            {
                ciphertext: payload                                                     // Pass CipherParams object
            }, 
            password,                                                                   
            {
                iv: iv
                //padding: CryptoJS.pad.NoPadding                                       // Apply PKCS7 padding
            });
        //return decrypted.toString();
        return decrypted.toString(CryptoJS.enc.Utf8);                                  // Utf8 decode plaintext
    }
    
    window.onload = function() {
        getPayload();
    };
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

    【讨论】:

    • 非常感谢!我找到了类似的解决方案,请检查! codepen.io/perec200/pen/NWbgJQa?editors=0012
    • 我看不出有什么不同。在您的新代码中,错误也已修复。差异是微不足道的(例如atob / btoa vs Base64 编码器或Latin1 vs UTF8 编码器,这与 ASCII 字符无关)。但是请注意,您仍在使用NoPadding,因此不会删除填充字节。在当前明文的情况下,这些是文本末尾的十个 0x0A 字节。您可以使用Hex 编码器而不是Latin1 来验证这一点。要删除这些填充字节,请使用 Pkcs7 填充(默认)。
    猜你喜欢
    • 2022-09-27
    • 2014-02-06
    • 2013-08-11
    • 1970-01-01
    • 2018-04-04
    • 2020-11-29
    • 1970-01-01
    • 2019-08-17
    • 2020-04-15
    相关资源
    最近更新 更多