使用 CryptoJS (JAVASCRIPT) 和 OpenSSL (PHP) 实现相同的加密
Achieve same encryption using CryptoJS (JAVASCRIPT) and OpenSSL (PHP)
我想在 ReactJS 应用程序中实现 PhP 加密功能。我需要以使用 OpenSSL 库函数 (openssl_encrypt) 创建的特定格式发送令牌。
与JAVASCRIPT 函数相比,PHP 函数生成的字符串短几个字符。当然,两者都获得相同的属性和特性。
PHP:
protected static function encrypt($stringData) {
$encrypted = false;
$encrypt_method = 'AES-256-CBC';
$iv = substr(hash('sha256', static::$ivMessage), 0, 16);
$encrypted= openssl_encrypt($stringData, $encrypt_method, static::$apiSecret, 0, $iv);
return $encrypted;
}
JAVASCRIPT:
export const encrypt = (stringData) => {
const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
const encrypted = CryptoJS.AES.encrypt(stringData, API_SECRET, {
iv,
mode: CryptoJS.mode.CBC,
pad: CryptoJS.pad.ZeroPadding,
});
return encrypted;
};
示例常量:
const stringData = "{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}";
const IV_MESSAGE = "a";
const API_SECRET = "secret_key";
(与 PHP 函数相同 --> $stringData, $ivMessage; $apiSecret)
如何实现在JAVASCRIPT中“复制”PHP函数?到目前为止我错过了什么?
CryptoJS 代码中的以下更改对于生成 PHP 代码的密文是必要的:
- 密钥必须作为
WordArray
传递。如果它作为字符串传递,它被解释为 passphrase,从中派生出 32 字节的密钥。
- PHP 用 0x00 值填充太短的键,直到指定长度。 CryptoJS 不会这样做,并且(由于 bug)通常在无效密钥的情况下为 AES 使用未定义的整数,因此预计不会有匹配的密文。
- PKCS7 填充用于 PHP 代码(见注释)。这也必须应用于 CryptoJS 代码,但是它是 default(以及 CBC 模式)。
以下PHP代码:
function encrypt($stringData) {
$ivMessage = "a";
$apiSecret = "secret_key";
$encrypted = false;
$encrypt_method = 'AES-256-CBC';
$iv = substr(hash('sha256', $ivMessage), 0, 16);
$encrypted= openssl_encrypt($stringData, $encrypt_method, $apiSecret, 0, $iv);
return $encrypted;
}
$stringData = '{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}';
print(encrypt($stringData) . "\n");
returns结果:
d/H+FfTaT/3tIkaXtIix937p6Df/vlnxagNJGJ7ljj48phT7oA7QssTatL3WNZY0Igt0r5ObGyCt0AR0IccVTFVZdR+nzNe+RmKQEoD4dj0mRkZ7qi/y3bAICRpFkP3Nz42fuILKApRtmZqGLTNO6dwlCbUVvjg59fgh0wCzy15g51G6CYLsEHa89Dt193g4qcXRWFgI9gyY1Gq7FX0G6Ers0fySQjjNcfDJg0Hj5aSxbPU6EPn14eaWqkliNYSMqzKhe0Ev7Y54x2YlUCNQeLZhwWRM2W0N+jGU7W+P/bCtF4Udwv4cweUESXkHLGtlQ0K6O5etVJDtb7ZtdEI/sA==
下面的 CryptoJS 代码生成相同密文:
const IV_MESSAGE = "a";
const API_SECRET = "secret_key[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]";
function encrypt(stringData){
const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
const encrypted = CryptoJS.AES.encrypt(
stringData,
CryptoJS.enc.Utf8.parse(API_SECRET),
{
iv: CryptoJS.enc.Utf8.parse(iv)
});
return encrypted;
};
const stringData = {"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true};
const ciphertextB64 = encrypt(JSON.stringify(stringData)).toString();
console.log(ciphertextB64.replace(/(.{64})/g,'\n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
还应考虑以下因素:
- 生成IV时避免将IV编码为十六进制字符串,直接使用二进制数据更可靠。否则,您还必须记住,根据平台的不同,通常可以应用十六进制数字的不同 upper/lower 大小写。这里这并不重要,因为在这两种情况下都使用小写字母。
- 如果你真的应该应用像 secret_key 这样的密码作为密钥,你还应该使用合理的密钥派生函数(例如 PBKDF2 结合随机生成的盐) 因为低熵。 CryptoJS 中使用的默认 KDF,即专有的 OpenSSL 函数
EVP_BytesToKey
,不应应用,因为它不是标准并且也被认为相对不安全。
- 出于安全原因,不能使用静态 IV。相反,应为每次加密应用随机生成的 IV。 IV 不是秘密的,通常以 IV 的顺序与密文连接,密文(见评论)。
我想在 ReactJS 应用程序中实现 PhP 加密功能。我需要以使用 OpenSSL 库函数 (openssl_encrypt) 创建的特定格式发送令牌。
与JAVASCRIPT 函数相比,PHP 函数生成的字符串短几个字符。当然,两者都获得相同的属性和特性。
PHP:
protected static function encrypt($stringData) {
$encrypted = false;
$encrypt_method = 'AES-256-CBC';
$iv = substr(hash('sha256', static::$ivMessage), 0, 16);
$encrypted= openssl_encrypt($stringData, $encrypt_method, static::$apiSecret, 0, $iv);
return $encrypted;
}
JAVASCRIPT:
export const encrypt = (stringData) => {
const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
const encrypted = CryptoJS.AES.encrypt(stringData, API_SECRET, {
iv,
mode: CryptoJS.mode.CBC,
pad: CryptoJS.pad.ZeroPadding,
});
return encrypted;
};
示例常量:
const stringData = "{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}";
const IV_MESSAGE = "a";
const API_SECRET = "secret_key";
(与 PHP 函数相同 --> $stringData, $ivMessage; $apiSecret)
如何实现在JAVASCRIPT中“复制”PHP函数?到目前为止我错过了什么?
CryptoJS 代码中的以下更改对于生成 PHP 代码的密文是必要的:
- 密钥必须作为
WordArray
传递。如果它作为字符串传递,它被解释为 passphrase,从中派生出 32 字节的密钥。 - PHP 用 0x00 值填充太短的键,直到指定长度。 CryptoJS 不会这样做,并且(由于 bug)通常在无效密钥的情况下为 AES 使用未定义的整数,因此预计不会有匹配的密文。
- PKCS7 填充用于 PHP 代码(见注释)。这也必须应用于 CryptoJS 代码,但是它是 default(以及 CBC 模式)。
以下PHP代码:
function encrypt($stringData) {
$ivMessage = "a";
$apiSecret = "secret_key";
$encrypted = false;
$encrypt_method = 'AES-256-CBC';
$iv = substr(hash('sha256', $ivMessage), 0, 16);
$encrypted= openssl_encrypt($stringData, $encrypt_method, $apiSecret, 0, $iv);
return $encrypted;
}
$stringData = '{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}';
print(encrypt($stringData) . "\n");
returns结果:
d/H+FfTaT/3tIkaXtIix937p6Df/vlnxagNJGJ7ljj48phT7oA7QssTatL3WNZY0Igt0r5ObGyCt0AR0IccVTFVZdR+nzNe+RmKQEoD4dj0mRkZ7qi/y3bAICRpFkP3Nz42fuILKApRtmZqGLTNO6dwlCbUVvjg59fgh0wCzy15g51G6CYLsEHa89Dt193g4qcXRWFgI9gyY1Gq7FX0G6Ers0fySQjjNcfDJg0Hj5aSxbPU6EPn14eaWqkliNYSMqzKhe0Ev7Y54x2YlUCNQeLZhwWRM2W0N+jGU7W+P/bCtF4Udwv4cweUESXkHLGtlQ0K6O5etVJDtb7ZtdEI/sA==
下面的 CryptoJS 代码生成相同密文:
const IV_MESSAGE = "a";
const API_SECRET = "secret_key[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]";
function encrypt(stringData){
const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
const encrypted = CryptoJS.AES.encrypt(
stringData,
CryptoJS.enc.Utf8.parse(API_SECRET),
{
iv: CryptoJS.enc.Utf8.parse(iv)
});
return encrypted;
};
const stringData = {"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true};
const ciphertextB64 = encrypt(JSON.stringify(stringData)).toString();
console.log(ciphertextB64.replace(/(.{64})/g,'\n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
还应考虑以下因素:
- 生成IV时避免将IV编码为十六进制字符串,直接使用二进制数据更可靠。否则,您还必须记住,根据平台的不同,通常可以应用十六进制数字的不同 upper/lower 大小写。这里这并不重要,因为在这两种情况下都使用小写字母。
- 如果你真的应该应用像 secret_key 这样的密码作为密钥,你还应该使用合理的密钥派生函数(例如 PBKDF2 结合随机生成的盐) 因为低熵。 CryptoJS 中使用的默认 KDF,即专有的 OpenSSL 函数
EVP_BytesToKey
,不应应用,因为它不是标准并且也被认为相对不安全。 - 出于安全原因,不能使用静态 IV。相反,应为每次加密应用随机生成的 IV。 IV 不是秘密的,通常以 IV 的顺序与密文连接,密文(见评论)。