如何通过 Crypto-JS 解密 AES 128-CBC?
How to decrypt AES 128-CBC by Crypto-JS?
我有一个 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代码中存在几个问题:
- 密钥不能作为字符串传递,而是作为
WordArray
传递(否则 CryptoJS 使用密钥派生函数)。
- IV和密文判断不正确
- 密文必须作为
CipherParams
对象(或 Base64 编码字符串)传递。
- 填充必须是 PKCS7(解密也适用于
NoPadding
,但不会删除填充字节)。
- 明文必须是utf8解码(.
toString()
默认十六进制编码)
有关详细信息,请参阅 CryptoJS 文档,尤其是章节 The Cipher Input and 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,'\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>
我有一个 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代码中存在几个问题:
- 密钥不能作为字符串传递,而是作为
WordArray
传递(否则 CryptoJS 使用密钥派生函数)。 - IV和密文判断不正确
- 密文必须作为
CipherParams
对象(或 Base64 编码字符串)传递。 - 填充必须是 PKCS7(解密也适用于
NoPadding
,但不会删除填充字节)。 - 明文必须是utf8解码(.
toString()
默认十六进制编码)
有关详细信息,请参阅 CryptoJS 文档,尤其是章节 The Cipher Input and 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,'\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>