我在一个文件中生成了一个随机的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
是包含32字节密钥的vector<unsigned char>
。我的问题是关于最后一个参数。它应该是密码算法的初始化向量。加密时我没有指定IV,所以一定使用了默认值。
为该参数传递nullptr是否意味着"使用默认值"?默认值是否为空,并且没有向第一个密码块添加任何内容?
我应该提到,我能够从命令行解密而不提供IV。
EVP_aes_256_cbc() [sic] cipher…
为该参数传递nullptr是否意味着"使用默认值"?默认值是否为空,并且没有向第一个密码块添加任何内容?
没有。你必须提供它。为了完整起见,IV应该是不可预测的。
Non-Predictable与Unique和Random略有不同。例如,SSLv3过去使用最后一块密文作为下一个块的IV。它是唯一的,但它既不是随机的也不是不可预测的,这使得SSLv3容易受到选择的明文攻击。
其他库做了一些聪明的事情,比如提供一个空向量(0的字符串)。攻击他们的人为此感谢他们。另请参阅为什么使用非随机IV与CBC模式是一个漏洞?如果使用已知和/或固定IV,则在CBC模式下是否安全?Crypto.SE。
/usr/bin/openssl enc -aes-256-cbc...
我应该提到,我能够从命令行解密而不提供IV。
OpenSSL使用内部mashup/密钥派生函数,该函数接受密码,并派生密钥和iv。它称为EVP_BytesToKey
,您可以在手册页中阅读到它。手册页也说:
如果总密钥和IV长度小于摘要长度,并且使用MD5,则派生算法与pkcs# 5 v1.5兼容,否则使用非标准扩展来派生额外的数据。
有很多EVP_BytesToKey
的例子,一旦你知道要找什么。如何在Java中使用AES对Openssl命令加密的Java文件进行解密
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))
加密时我没有指定IV,所以一定使用了默认值。
检查返回值。呼叫应该在路径的某个地方失败了。也许不是在EVP_DecryptInit_ex
,但肯定在EVP_DecryptFinal
之前。
如果没有失败,请提交错误报告
EVP_DecryptInit_ex
是AES解密原语的接口。这只是解密OpenSSL加密格式所需的一部分。OpenSSL加密格式没有很好的文档,但是您可以从代码和一些文档中反向工作。密钥和IV计算在EVP_BytesToKey
文档中解释:
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+密钥对永远不要重复。OpenSSL进程确保只要随机盐不再重复。
以这种方式使用MD5可能会以泄露信息的方式将密钥和IV纠缠在一起,但我从未见过这样的分析。如果您必须使用OpenSSL格式,我将毫不犹豫地使用它的第四代。OpenSSL格式的大问题是它的暴力破解速度很快(4轮MD5不够扩展),而且它缺乏任何身份验证。