实施了具有意外价值的 AES 256
Implemented AES 256 with unexpected value
我正在尝试通过 JavaScript、CryptoJS.AES.encrypt 实现 AES-256 加密,并希望获得与 php 实现的代码相同的结果。然而,我不仅得到了错误的答案,而且字符串长度也不一致。
这是我的 JavaScript 代码,其中包含我使用的参数、密钥和 iv。
let parameter = {
MerchantID: "3430112",
RespondType: "JSON",
TimeStamp: "1485232229",
Version: "1.4",
MerchantOrderNo: "S_1485232229",
Amt: 40,
ItemDesc: "UnitTest",
}
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";
const CryptoJS = require('crypto-js');
// ------------------INFO----------------------------------------
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.Pkcs7 || CryptoJS.pad.ZeroPadding
};
let result = create_mpg_aes_encrypt(parameter, key, encrypt_mode);
//console.log(result.length);
//console.log(result);
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);
let encryptedData = CryptoJS.AES.encrypt(str, key, encrypt_mode);
encryptedData = encryptedData.toString();
//console.log(encryptedData.length);
//console.log(encryptedData);
str = encryptedData;
str = str.trim();
return str;
}
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;
}
// The result I really get.
/*
/5HIqgE3nk3mIaROXxH3Lk0lvbGhgkLbbO+e8H2AsBZeR2/R2ayqUxcCcsgtEilh4aBwCnQnz6HPkNt/bWWTu8kxAqTUubZtmXTBPDGnq0u6HU4HkPDLu9etZMbTyAEqYBzqqAi/9w+UqO+lpPmEudQTBP/YeWEhd8Yi919CFPr5gMs1sJrNNskJ560tkazr
*/
这是我想要实现的 php 代码。
function create_mpg_aes_encrypt($parameter = "", $key = "", $iv = ""){
$return_str = '';
if(!empty($parameter)) {
// URL ENCODED QUERY STRING
$return_str = http_build_query($parameter);
//echo $return_str;
}
return trim(bin2hex(openssl_encrypt(addpadding($return_str), 'aes-256-cbc',
$key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv)));
}
function addpadding($string, $blocksize = 32) {
$len = strlen($string);
$pad = $blocksize - ($len % $blocksize);
$string .= str_repeat(chr($pad), $pad);
return $string;
}
$trade_info_arr = array(
'MerchantID' => 3430112,
'RespondType' => 'JSON',
'TimeStamp' => 1485232229,
'Version' => 1.4,
'MerchantOrderNo' => 'S_1485232229',
'Amt' => 40,
'ItemDesc' => 'UnitTest'
);
$mer_key ='12345678901234567890123456789012';
$mer_iv = '1234567890123456';
// Result I expected to get
/*ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa*/
在此处输入代码
CryptoJS 代码中只有两个小错误:
必须禁用默认填充(AES 块大小为 16 字节的 PKCS7),因为在 addpadding()
中实现了自定义填充(块大小为 32 字节的 PKCS7) :
padding: CryptoJS.pad.ZeroPadding
密文必须返回十六进制编码,而不是 Base64 编码:
encryptedData = encryptedData.ciphertext.toString(CryptoJS.enc.Hex);
通过这两项更改,CryptoJS 代码可以正常工作并提供所需的结果:
let parameter = {
MerchantID: "3430112",
RespondType: "JSON",
TimeStamp: "1485232229",
Version: "1.4",
MerchantOrderNo: "S_1485232229",
Amt: 40,
ItemDesc: "UnitTest",
}
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";
//const CryptoJS = require('crypto-js');
// ------------------INFO----------------------------------------
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.Pkcs7 || CryptoJS.pad.ZeroPadding // Fix 1: Disable padding
padding: CryptoJS.pad.ZeroPadding
};
let result = create_mpg_aes_encrypt(parameter, key, encrypt_mode);
//console.log(result.length);
console.log(result.replace(/(.{48})/g,'\n'));
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);
let encryptedData = CryptoJS.AES.encrypt(str, key, encrypt_mode);
//encryptedData = encryptedData.toString(); // Fix 2: Hex encode ciphertext
encryptedData = encryptedData.ciphertext.toString(CryptoJS.enc.Hex);
//console.log(encryptedData.length);
//console.log(encryptedData);
str = encryptedData;
str = str.trim();
return str;
}
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;
}
// The result I really get.
/*
/5HIqgE3nk3mIaROXxH3Lk0lvbGhgkLbbO+e8H2AsBZeR2/R2ayqUxcCcsgtEilh4aBwCnQnz6HPkNt/bWWTu8kxAqTUubZtmXTBPDGnq0u6HU4HkPDLu9etZMbTyAEqYBzqqAi/9w+UqO+lpPmEudQTBP/YeWEhd8Yi919CFPr5gMs1sJrNNskJ560tkazr
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
请注意,PHP 参考代码不必要地应用 32 字节的块大小来填充。通常填充到所用算法的块大小,AES 为 16 字节。填充到 32 字节不是错误,但它会不必要地增加密文的大小。
此外,静态 IV 是不安全的(必须为每次加密随机生成 IV),同样密码作为密钥(在密码的情况下,使用基于密码的密钥派生函数,例如 Argon2 或PBKDF2), 虽然发布的密钥和 IV 可能仅用于测试目的,这当然可以。
我正在尝试通过 JavaScript、CryptoJS.AES.encrypt 实现 AES-256 加密,并希望获得与 php 实现的代码相同的结果。然而,我不仅得到了错误的答案,而且字符串长度也不一致。
这是我的 JavaScript 代码,其中包含我使用的参数、密钥和 iv。
let parameter = {
MerchantID: "3430112",
RespondType: "JSON",
TimeStamp: "1485232229",
Version: "1.4",
MerchantOrderNo: "S_1485232229",
Amt: 40,
ItemDesc: "UnitTest",
}
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";
const CryptoJS = require('crypto-js');
// ------------------INFO----------------------------------------
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.Pkcs7 || CryptoJS.pad.ZeroPadding
};
let result = create_mpg_aes_encrypt(parameter, key, encrypt_mode);
//console.log(result.length);
//console.log(result);
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);
let encryptedData = CryptoJS.AES.encrypt(str, key, encrypt_mode);
encryptedData = encryptedData.toString();
//console.log(encryptedData.length);
//console.log(encryptedData);
str = encryptedData;
str = str.trim();
return str;
}
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;
}
// The result I really get.
/*
/5HIqgE3nk3mIaROXxH3Lk0lvbGhgkLbbO+e8H2AsBZeR2/R2ayqUxcCcsgtEilh4aBwCnQnz6HPkNt/bWWTu8kxAqTUubZtmXTBPDGnq0u6HU4HkPDLu9etZMbTyAEqYBzqqAi/9w+UqO+lpPmEudQTBP/YeWEhd8Yi919CFPr5gMs1sJrNNskJ560tkazr
*/
这是我想要实现的 php 代码。
function create_mpg_aes_encrypt($parameter = "", $key = "", $iv = ""){
$return_str = '';
if(!empty($parameter)) {
// URL ENCODED QUERY STRING
$return_str = http_build_query($parameter);
//echo $return_str;
}
return trim(bin2hex(openssl_encrypt(addpadding($return_str), 'aes-256-cbc',
$key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv)));
}
function addpadding($string, $blocksize = 32) {
$len = strlen($string);
$pad = $blocksize - ($len % $blocksize);
$string .= str_repeat(chr($pad), $pad);
return $string;
}
$trade_info_arr = array(
'MerchantID' => 3430112,
'RespondType' => 'JSON',
'TimeStamp' => 1485232229,
'Version' => 1.4,
'MerchantOrderNo' => 'S_1485232229',
'Amt' => 40,
'ItemDesc' => 'UnitTest'
);
$mer_key ='12345678901234567890123456789012';
$mer_iv = '1234567890123456';
// Result I expected to get
/*ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa*/
在此处输入代码
CryptoJS 代码中只有两个小错误:
必须禁用默认填充(AES 块大小为 16 字节的 PKCS7),因为在
addpadding()
中实现了自定义填充(块大小为 32 字节的 PKCS7) :padding: CryptoJS.pad.ZeroPadding
密文必须返回十六进制编码,而不是 Base64 编码:
encryptedData = encryptedData.ciphertext.toString(CryptoJS.enc.Hex);
通过这两项更改,CryptoJS 代码可以正常工作并提供所需的结果:
let parameter = {
MerchantID: "3430112",
RespondType: "JSON",
TimeStamp: "1485232229",
Version: "1.4",
MerchantOrderNo: "S_1485232229",
Amt: 40,
ItemDesc: "UnitTest",
}
const Hashkey = "12345678901234567890123456789012";
const HashIV = "1234567890123456";
//const CryptoJS = require('crypto-js');
// ------------------INFO----------------------------------------
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.Pkcs7 || CryptoJS.pad.ZeroPadding // Fix 1: Disable padding
padding: CryptoJS.pad.ZeroPadding
};
let result = create_mpg_aes_encrypt(parameter, key, encrypt_mode);
//console.log(result.length);
console.log(result.replace(/(.{48})/g,'\n'));
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);
let encryptedData = CryptoJS.AES.encrypt(str, key, encrypt_mode);
//encryptedData = encryptedData.toString(); // Fix 2: Hex encode ciphertext
encryptedData = encryptedData.ciphertext.toString(CryptoJS.enc.Hex);
//console.log(encryptedData.length);
//console.log(encryptedData);
str = encryptedData;
str = str.trim();
return str;
}
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;
}
// The result I really get.
/*
/5HIqgE3nk3mIaROXxH3Lk0lvbGhgkLbbO+e8H2AsBZeR2/R2ayqUxcCcsgtEilh4aBwCnQnz6HPkNt/bWWTu8kxAqTUubZtmXTBPDGnq0u6HU4HkPDLu9etZMbTyAEqYBzqqAi/9w+UqO+lpPmEudQTBP/YeWEhd8Yi919CFPr5gMs1sJrNNskJ560tkazr
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
请注意,PHP 参考代码不必要地应用 32 字节的块大小来填充。通常填充到所用算法的块大小,AES 为 16 字节。填充到 32 字节不是错误,但它会不必要地增加密文的大小。
此外,静态 IV 是不安全的(必须为每次加密随机生成 IV),同样密码作为密钥(在密码的情况下,使用基于密码的密钥派生函数,例如 Argon2 或PBKDF2), 虽然发布的密钥和 IV 可能仅用于测试目的,这当然可以。