14

我在一个文件中生成了一个随机的 256 位对称密钥,用于使用 OpenSSL 命令行加密一些数据,稍后我需要使用 OpenSSL 库以编程方式对其进行解密。我没有成功,我认为问题可能出在我正在使用(或未使用)的初始化向量中。

我使用以下命令加密数据:

/usr/bin/openssl enc -aes-256-cbc -salt -in input_filename -out output_filename -pass file:keyfile

我正在使用以下调用来初始化数据的解密:

EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))

keyfile是一个vector<unsigned char>保存 32 个字节的密钥。我的问题是关于最后一个参数。它应该是密码算法的初始化向量。加密时我没有指定 IV,所以必须使用一些默认值。

为该参数传递 nullptr 是否意味着“使用默认值”?默认为空,第一个密码块中没有添加任何内容?

我应该提一下,我可以在不提供 IV 的情况下从命令行解密。

4

2 回答 2

9

使用 EVP_aes_256_cbc() [sic] 密码加密时的默认 IV 是什么...

为该参数传递 nullptr 是否意味着“使用默认值”?默认为空,第一个密码块中没有添加任何内容?

空无一人。你必须提供它。为了完整起见,IV 应该是不可预测的。

Non-Predictable与UniqueRandom略有不同。例如,SSLv3 曾经将最后一个密文块用于下一个块的 IV。它是Unique,但既不是Random也不是Non-Predictable,它使 SSLv3 容易受到选择的明文攻击。

其他库会做一些聪明的事情,比如提供一个空向量(一串 0)。他们的攻击者为此感谢他们。另请参阅为什么将非随机 IV 与 CBC 模式一起使用是一个漏洞?关于堆栈溢出,如果使用已知和/或固定的 IV,CBC 模式下的 AES 是否安全?在 Crypto.SE 上。


/usr/bin/openssl enc -aes-256-cbc...

我应该提一下,我可以在不提供 IV 的情况下从命令行解密。

OpenSSL 使用内部混搭/密钥派生函数,该函数接受密码,并派生密钥和 iv。它被称为EVP_BytesToKey,您可以在手册页中阅读它。手册页还说:

如果总密钥和 IV 长度小于摘要长度并且使用 MD5,则派生算法与 PKCS#5 v1.5 兼容,否则使用非标准扩展来派生额外数据。

EVP_BytesToKey一旦你知道要寻找什么,就有很多例子。密钥的 Openssl 密码是 C 中的一个。如何在 Java 中使用 AES 在 Java 中解密使用 openssl 命令加密的文件


EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))

加密时我没有指定 IV,所以必须使用一些默认值。

检查您的返回值。呼叫应该在路径上的某个地方失败。也许不是在EVP_DecryptInit_ex,但肯定是在之前EVP_DecryptFinal

如果它没有失败,那么请提交错误报告。

于 2016-06-30T15:43:32.073 回答
8

EVP_DecryptInit_ex是 AES 解密原语的接口。这只是解密 OpenSSL 加密格式所需的一部分。OpenSSL 加密格式没有很好的文档记录,但是您可以从代码和一些文档中反向使用它。EVP_BytesToKey文档中解释了密钥和 IV 计算:

   The key and IV is derived by concatenating D_1, D_2, etc until enough
   data is available for the key and IV. D_i is defined as:

           D_i = HASH^count(D_(i-1) || data || salt)

   where || denotes concatentaion, D_0 is empty, HASH is the digest
   algorithm in use, HASH^1(data) is simply HASH(data), HASH^2(data) is
   HASH(HASH(data)) and so on.

   The initial bytes are used for the key and the subsequent bytes for the
   IV.

这里的“HASH”是MD5。实际上,这意味着您可以像这样计算哈希:

Hash0 = ''
Hash1 = MD5(Hash0 + Password + Salt)
Hash2 = MD5(Hash1 + Password + Salt)
Hash3 = MD5(Hash2 + Password + Salt)
...

然后提取密钥所需的字节,然后提取 IV 所需的字节。对于 AES-128,这意味着 Hash1 是密钥,Hash2 是 IV。对于 AES-256,密钥是 Hash1+Hash2(连接,未添加),Hash3 是 IV。

您需要剥离前导Salted___标头,然后使用盐来计算密钥和 IV。然后你将有碎片喂入EVP_DecryptInit_ex

但是,由于您是在 C++ 中执行此操作,因此您可能只需挖掘enc代码并重用它(在验证其许可证与您的使用兼容之后)。

请注意,OpenSSL IV 是随机生成的,因为它是涉及随机盐的散列过程的输出。第一个区块的安全性不依赖于 IV 本身是随机的;它只要求永远不要重复特定的 IV+Key 对。OpenSSL 过程确保只要随机盐永远不会重复。

以这种方式使用 MD5 可能会以泄露信息的方式将密钥和 IV 纠缠在一起,但我从未见过这样的分析。如果你必须使用 OpenSSL 格式,我不会对它的 IV 代有任何犹豫。OpenSSL 格式的大问题是它的暴力破解速度很快(4 轮 MD5 拉伸不够)并且它缺乏任何身份验证。

于 2016-06-30T14:37:58.550 回答