Crypto-js 本地到 php 服务器端

Crypto-js local side to php server side

我有一个使用 crypto-Js AES 的应用程序。模拟工作代码为:

var ciphertext = CryptoJS.AES.encrypt('My_message', 'My_secret_key');
console.log(ciphertext.toString()); 

答案是:

U2FsdGVkX1/Dd3uAr/mdw5lVoBvq0UX5LHnNoX24JAM=

当我尝试在服务器端重现它时,我从未得到相同的答案:

$passphrase='My_secret_key';
$value='My_message';
$salt = openssl_random_pseudo_bytes(8);
$salt ='';
$salted = '';
$dx = '';
while (strlen($salted) < 48) {
 $dx = md5($dx.$passphrase.$salt, true);
 $salted .= $dx;
}
$key = substr($salted, 0, 32);
$iv  = substr($salted, 32,16);
$encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, true, $iv);
echo base64_encode($encrypted_data);

服务器端回答:

3jSTl1yR55lfTbz7f0o3Yw==

我一定错过了什么,但又不能指出是什么。本地端不能碰。 欢迎所有帮助

我以前遇到过你的确切情况。我们很难在 PHP 和 Java(Android)上获得相同的结果。许多天在 2 个公司的许多开发人员。没有运气。

我们最终从 PHP 调用了 CryptoJS。如果我没记错的话,Crypto JS 中有一些非标准的东西。我可能错了,那是前一段时间。

通过 PHP 使用节点调用 CryptoJS 可能是一个更强大的解决方案。然后您可以使用确保兼容性的相同库。

$result = shell_exec('node yourCryptoJSprogram.js');

我们确实达到了可以通过这种方式作为参数传递的数据量的限制。我建议使用 PHP 写入文件并再次使用 NodeJS 读取。

如果性能出现问题,请考虑 运行 Express 服务器并从 PHP 进行 REST 调用。

如果该答案不能满足您的要求,请考虑使用或复制我编写的这个简单的基于 OpenSSL 的 PHP 库来找出答案: https://github.com/io-digital/php-crypto

如果 CryptoJS.AES.encrypt 中的第二个参数作为字符串传递,它被解释为密码短语,从中派生出实际密钥和 IV,[1]. This is achieved by using the functionality of the OpenSSL-function EVP_BytesToKey with an iteration count of 1 and the MD5-digest, [2] [3] (note that CryptoJS doesn't consider the switch of the default digest from MD5 to SHA256 from OpenSSL version 1.1.0c on, [4])。

CryptoJS.AES.encrypt returns一个CipherParams-object,封装了密文、密钥、IV和盐,[5]。此外 CipherParams#toString() returns 中的结果 OpenSSL-format 为 Base64 编码的字符串。 OpenSSL-format由一个16字节的header和后面的密文组成。 header 以 ASCII-encoded 字符串 Salted__ 开头,后跟一个 8 字节的盐。 salt 每次随机生成,并与密码一起用于派生密钥/IV。这每次都会创建一个不同的密钥/IV。

PHP-code 在功能上是相同的:Key 和 IV 每次都使用新生成的盐从密码短语中通过模拟逻辑导出(证明见下文)。但是,需要进行一些小改动:

  • 必须删除以下行:$salt ='';

  • 目前的代码只显示Base64编码的密文。对于 OpenSSL-format 中结果的 Base64 编码输出,代码必须改为:

    echo base64_encode('Salted__'.$salt.$encrypted_data); 
    
  • openssl_encrypt 中的第 4 个参数应从 true 更改为 OPENSSL_RAW_DATA。两者在功能上完全相同,但OPENSSL_RAW_DATA的使用更加透明。

JavaScript- 和 PHP-code 每次都会生成一个新的盐,因此会生成不同的密钥和 IV,每次都会更改密文。事情本该如此。由于盐与密文一起存储,因此可以随时使用密码来解密密文。

证明两个代码使用相同的逻辑推导密钥和IV:每次生成的新盐/密文防止直接比较两个代码的结果。为了不费力地执行此比较,最好在 PHP-code 中也使用 JavaScript-code 中生成的盐。 JavaScript-code中的盐可以确定为十六进制字符串:

console.log(ciphertext.salt.toString(CryptoJS.enc.Hex)); 

这个salt是要在PHP-code中使用的,而不是随机生成的salt(当然只针对这一比较):

$salt = hex2bin('<Salt from JavaScript-Code as hexadecimal string>'); 

两个输出的比较现在证明它们是相等的,表明两个代码在功能上是相同的。