将 PHP openssl_encrypt 与 JavaScript 中的空白 IV 匹配

Match PHPs openssl_encrypt with blank IV in JavaScript

当 PHP 中没有声明 IV 并且使用了不合规的密钥长度时,如何在 JavaScript 中匹配 openssl_encrypt 的输出?

PHP

php -r '$value = openssl_encrypt("test", "AES-128-CBC", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); echo $value;'

/u+5lB/VwbMX9U1YY4cnCQ==

JavaScript

iv  = CryptoJS.enc.Utf8.parse("");
var encrypted = CryptoJS.AES.encrypt("test", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
console.log(encrypted.toString());

U2FsdGVkX19Cn1f6x8C/rJdfwzsZk5m5WWCUrR4z3U4=

console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));

l1/DOxmTmblZYJStHjPdTg==

我只能修改 Javascript 代码,我需要一种方法来加密字符串,以便它可以被 PHP 的 openssl_decrypt 解密。如果字符串是从 PHP.

加密的,它显然工作正常

我知道不声明 IV 会降低代码的安全性,但在我的特定情况下这不是什么大问题。

我在其他主题中读到 PHP 默认填充为 Pkcs7,这就是我将其添加到 JS 方法的原因。

我目前的理论是 PHP 的默认 IV 是问题所在,或者 JS 函数的输出需要进一步处理。如您所见,我尝试了 Base64.stringify 方法,但结果仍然不同。

我在这里使用的示例密钥与实际密钥的长度相同。

我正在使用https://github.com/sytelus/CryptoJS/blob/master/rollups/aes.js(无论我在JS中使用什么,它都需要像独立文件一样易于分发,占用空间相对较小)

您的代码存在一些问题:

  • 如果您想使用现有密钥,则需要向 CryptoJS.<cipher>.encrypt 提供一个 WordArray 对象。这意味着必须以某种方式对其进行解析。如果您简单地提供一个字符串作为 "key",那么 CryptoJS 将假定它是一个密码,生成一个随机盐并使用 EVP_BytesToKey 从密码和盐中导出密钥和 IV。
  • 您的 "key" 长度为 29 个字符。 AES 仅支持三种密钥大小:16、24 和 32 字节。由于您在 PHP 中使用了 AES-128,因此您需要在 CryptoJS 中提供一个 16 字节的密钥,以便自动选择 AES-128。请记住:密钥应该是随机选择的,并且与随机噪声无法区分,因此它具有某种安全性。如果必须以某种方式打印密钥,请使用 Hex 或 Base64 编码。

完整示例:

var iv  = CryptoJS.enc.Utf8.parse("");
var key = CryptoJS.enc.Utf8.parse("aaaaaaaaaaaaaaaa");
var encrypted = CryptoJS.AES.encrypt("test", key, {
    iv: iv, 
    mode: CryptoJS.mode.CBC, 
    padding: CryptoJS.pad.Pkcs7
});

console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>