Crypto-js本地端到PHP服务器端

时间:2019-12-05 17:04:43

标签: javascript php cryptography aes

我有一个使用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==

我必须错过一些事情,但不能指出。本地无法触及。 欢迎所有帮助

2 个答案:

答案 0 :(得分:0)

我以前一直在你的情况下。我们很难在PHP和Java(适用于Android)上获得相同的结果。许多公司的开发人员在2天的时间里。没运气。

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

通过PHP用node调用CryptoJS可能是一个更可靠的解决方案。然后,您可以使用同一库来确保兼容性。

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

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

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

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

答案 1 :(得分:0)

如果CryptoJS.AES.encrypt中的第二个参数作为字符串传递,则将其解释为密码短语,从中导出实际的密钥和IV,[1]。这是通过使用OpenSSL函数EVP_BytesToKey的功能(迭代计数为1和MD5摘要,[2] [3])来实现的(请注意,CryptoJS不会考虑从[4]的OpenSSL版本1.1.0c将默认摘要从MD5切换到SHA256。

CryptoJS.AES.encrypt返回一个CipherParams对象,该对象封装了密文,密钥IV和盐[5]。另外,CipherParams#toString()以OpenSSL格式作为Base64编码的字符串返回结果。 OpenSSL格式由一个16字节的标头和随后的密文组成。标头以ASCII编码的字符串Salted__开头,后跟8字节盐。每次都会随机生成该盐,并将其与密码一起使用以得出密钥/ IV。每次创建一个不同的键/ IV。

PHP代码在功能上是相同的:每次使用新生成的盐从口令短语中通过模拟逻辑得出Key和IV(有关证明,请参见下文)。但是,必须进行一些小的更改:

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

  • 在当前代码中,仅显示Base64编码的密文。对于以OpenSSL格式对结果进行Base64编码的输出,必须改用以下代码:

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

JavaScript和PHP代码每次都会生成一个新的盐,因此会生成不同的密钥和IV,从而每次都会更改密文。那就是应该的样子。由于盐与密文一起存储,因此可以随时使用密码对密文进行解密。

证明这两个代码使用相同的逻辑来导出密钥和IV:每次生成的新盐/密文均无法直接比较两个代码的结果。为了毫不费力地执行此比较,最好也使用PHP代码中JavaScript代码中生成的盐。 JavaScript代码中的盐可以通过以下方式确定为十六进制字符串:

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

此盐应在PHP代码中使用,而不是随机生成的盐(当然仅用于此比较):

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

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