使用 CryptoJS 解密 openssl AES

Decrypt openssl AES with CryptoJS

我正在尝试使用 CryptoJS 3.1.5.

解密使用 openssl 加密的文件

如果我使用 CryptoJS 加密和解密,一切正常,shell 中的 OpenSSL 也是如此,但是当我尝试将 CryptoJS 与 OpenSSL 混合使用时,一切都出错了。

文件是使用此命令创建的:

openssl enc -aes-256-cbc -in file.txt -out file.enc -k password

我试着这样解密:

fs.readFile('file.enc', function(err, data) {
  var decrypted = CryptoJS.AES.decrypt(
                    data.toString(),
                    "password",
                    { mode : CryptoJS.mode.CBC }
                  );

  console.log(decrypted.toString(CryptoJS.enc.Utf8));
});

// Give me this err: Uncaught Error: Malformed UTF-8 data

换句话说,我是这样的:

fs.readFile('file.txt', function(err, data) {
  var encrypted = CryptoJS.AES.encrypt(
                    data.toString(),
                    "password",
                    { mode : CryptoJS.mode.CBC });

  fs.writeFile('file.enc', encrypted);
});

然后在Shell:

openssl enc -d -aes-256-cbc -in file.enc -out file2.txt -k password
// Give me this err: bad magic number

我是不是遗漏了什么明显的东西?

还不是绝对的答案,但评论太多了:

命令行 openssl enc 默认情况下使用带盐的基于密码的加密 (PBE),这意味着实际的加密密钥和适用于 CBC 的 IV 是根据给定密码和随机数计算得出的Password Based Key Derivation Function 的盐值,使对手更难尝试密码猜测攻击。我不知道你的 JS 模块(或根本不了解 JS),但你 link 的网页列出了各种低级原语,表明它不会自动执行 PBE。像 "password" 这样的文本字符串(可能)适用于 PBE,但不适用于直接 AES 加密,其中密钥必须正好是 128、192 或 256 位,并且应该是随机二进制数据。

如果要openssl的半标准PBE,在JS端匹配; evpkey 项听起来可能有帮助,因为 EVP 是涉及的 openssl 模块,而且我不知道其他 (PB)KDF 方案会被称为 EVP。如果不是,enc 默认 PBE 只是密码的 MD5 与 salt 连接,根据需要迭代反馈多次,在本例中为 3。请参阅 https://superuser.com/questions/455463/openssl-hash-function-for-generating-aes-key 以获取(大部分)perl 中的示例。 OpenSSL 为文件添加了 8 个 ASCII 字符 "Salted__" 和 8 个字节的加盐前缀,因此您需要在解密前删除它们(并使用加盐),或在加密后添加它们。

如果你想要原始加密,选择一个更合适的密钥(无论哪一边),以及一个独特且不可预测的IV,除非你总是使用新密钥,在这种情况下你可以使用固定 IV,并在 openssl 端使用 -K(注意大写)和 -iv 以十六进制 指定这些值 。请参阅安装了 openssl 或 https://www.openssl.org/docs/manmaster/apps/enc.html 的任何 Unix 系统的联机帮助页。

Plus 在任何一种情况下 enc 默认为 "PKCS#5"(真正的 PKCS#7)padding。我不知道你的 JS 模块是否支持;如果不是,你应该指定它。除非你能保证你的明文总是 16 字节的精确倍数(在像 UTF8 这样的任何编码之后);那么你可以在 JS 端指定(或者默认)没有填充,并在 openssl 端指定 -nopad

此PHP代码,其中包括命令行OpenSSL加密:

http://pastebin.com/sivmZvSw

...与此 CryptoJS 代码兼容,我通常在命令行上 运行 在我的 Mac 上使用 jsc :

http://pastebin.com/LcDBG7yj

(此代码是用 CryptoJS 3.1.2 编写的,尽管我认为它与 3.1.5 之间没有重大区别。)

技巧是:

  1. 如另一个答案所示,您需要在两侧指定确切的密钥和 IV 才能正常工作。

  2. 尽管 AES-256 理论上可以处理 128 位密钥,但我发现似乎只有 256 位密钥有效。

  3. 在这些示例中,我避免使用盐值。可以说您可以通过包含盐来使其更安全,但请确保您在两个地方都正确指定了它。

  4. 另一件让人们感到困惑的事情是他们认为他们可以将字符串传递给 CryptoJS.decrypt() 函数。这是不正确的。 CryptoJS.decrypt() 需要一个 cipherParams 对象。 (参见示例。)

  5. 关于填充:CryptoJSOpenSSL都默认为PKCS#7,除了PKCS#7可以处理任何块大小外,它在功能上等同于PKCS#5。当我们谈论 8 字节块大小时,它们是相同的。无论如何,您不需要在 CrytoJS.

  6. 中指定填充

祝你好运!

郑重声明,这就是我解密 openssl 文件的方式。

//openssl enc -aes-256-cbc -in file.txt -out file.enc -k password

fs.readFile('file.enc', function(err, data) {
  var salt          = data.toString("hex", 8, 16),
      enc           = data.toString("hex", 16, data.length),
      derivedParams = CryptoJS.kdf.OpenSSL.execute(
                        password,
                        256/32,
                        128/32,
                        CryptoJS.enc.Hex.parse(salt)
                      ),
      cipherParams  = CryptoJS.lib.CipherParams.create({
                       ciphertext : CryptoJS.enc.Hex.parse(enc)
                     }),
      decrypted     = CryptoJS.AES.decrypt(
                        cipherParams,
                        derivedParams.key,
                        { iv : derivedParams.iv }
                      );

  console.log(hex2a(decrypted.toString())); // result is in hexa
});

这就是我加密以使其与 OpenSSL 一起使用的方式

fs.readFile('file.txt', function(err, data) {
  var encrypted = CryptoJS.AES.encrypt(data.toString(), password);
      buff      = new Buffer(encrypted.toString(), "base64");

  fs.writeFile('file.enc', buff);
});

// openssl enc -d -aes-256-cbc -in file.enc -out file2.txt -k password

希望对大家有所帮助:)

您应该在 shell 命令中使用 -K 70617373776F7264 而不是 -k password70617373776F7264是密码的十六进制代码。