我正在尝试解密一些使用PHP脚本中的密码和aes-256-cbc
方法加密的数据。
这是我加密原始数据的方式
printf "Hello" | openssl enc -e -base64 -A -aes-256-cbc -k "MYPASSWORD"
// output
U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=
当我尝试在命令行中对其解密时,
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD"
// output
Hello
但是当我在我的 PHP 脚本中使用openssl_decrypt()时,它不起作用!
$result = openssl_decrypt("U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=", 'AES-256-CBC', "MYPASSWORD");
var_dump($result);
//output
bool(false)
我添加以下几行以获取错误
while ($msg = openssl_error_string())
echo $msg . "<br />\n";
它返回:
错误:06065064:数字信封例程:EVP_DecryptFinal_ex:错误 解密
我知道我应该使用密钥/ iv对,但是我无法从密码中提取任何盐。 如何使以下命令生效?
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -K ??????????????? -iv ????????????????
// expected output !!!
Hello
编辑:
我试图通过-p
参数获取key / iv,但是它不起作用
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD" -p
salt=9D5AE06E8A2B627C
key=8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA
iv =4150125DCCD36F73A9F08F3020151A04
Hello
printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -K 8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF05E67E2A8313FA -iv 4150125DCCD36F73A9F08F3020151A04
bad decrypt
140735954895816:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:529:
??G?"r!C???&C&??
答案 0 :(得分:2)
通过openssl enc
选项(在您的情况下为-k
)用作"MYPASSWORD"
参数的密码(或密码)与key
参数之间存在差异PHP函数openssl_decrypt()
所期望的。 -k
的{{1}}选项是任意长度的密码,将从中得出实际的256位加密密钥。这也是PHP openssl enc
函数所需要的关键。该加密密钥为256位,因为您选择了openssl_decrypt()
。
通过调用aes-256
时添加-p
选项,您可以了解派生的加密密钥是什么。这还将打印openssl enc
,这是PHP iv
函数需要使用的另一个参数。例如:
openssl_decrypt()
这些打印的printf "Hello" | openssl enc -e -base64 -A -aes-256-cbc -k "MYPASSWORD" -nosalt -p
key=E0FAC2DD2C00FFE30F27A6D14568CB4F12EB84676A3A2BFB172A444C3BBB831F
iv =5A79774BB4B326EED949E6871FC27697
sp0z18QezUO8tSy7tgjOEw==
和key
值是您需要输入到PHP iv
函数调用中的值,如下所示:
openssl_decrypt()
现在运行PHP脚本会成功:
$ciphertext = 'sp0z18QezUO8tSy7tgjOEw==';
$key = hex2bin('E0FAC2DD2C00FFE30F27A6D14568CB4F12EB84676A3A2BFB172A444C3BBB831F');
$iv = hex2bin('5A79774BB4B326EED949E6871FC27697');
$result = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, 0, $iv);
var_dump($result);
运行$ php decrypt.php
string(5) "Hello"
时,您可能已经注意到了额外的-nosalt
选项。 Salt用于在密钥派生过程中添加一些随机性/唯一性,openssl enc
省略了该步骤。结果,-nosalt
,key
和iv
在每次运行中都将是相同的(如果使用相同的密码和明文),那么您应该能够准确地再现输出。如果您不使用ciphertext
,您的实验仍然可以进行,但是每次运行的-nosalt
,key
和iv
值将有所不同,并且您还必须摆脱掉ciphertext
作为标题添加的盐中的盐-有关详细信息,请参阅此答案的进一步内容。
另一个选择是让PHP代码在调用openssl
之前从密码短语中导出key
和iv
。为此,您将必须检查code of the enc
tool所使用的openssl_decrypt()
版本。在这里,您可以看到使用了哪个密钥派生函数-它取决于您使用的openssl
的版本以及您提供的选项-以及是否在{{1 }}。
更新,响应您的评论,在其中添加以下信息:您只有密文和密码可用,并且密文是使用openssl
创建的。
在查看openssl
的源代码时,它提到in a comment in the source file evpkdf.js
,“密钥派生函数旨在符合EVP_BytesToKey”,这与大多数crypto-js
版本使用的功能相同。因此,您应该能够使用crypto-js
工具使用openssl
选项来提取openssl enc
和key
,如下所示:
iv
(您现在也已在另一条注释中确认),然后如上所述在调用PHP函数时使用它们。请注意,您将必须对每个密文分别进行此操作,因为-p
对$printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl enc -d -base64 -A -aes-256-cbc -k "MYPASSWORD" -p
salt=9D5AE06E8A2B627C
key=8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA
iv =4150125DCCD36F73A9F08F3020151A04
(以及salt
和key
)的选择是不同的,并且每次加密都是随机选择的行动。要直接在PHP中执行此操作,请参阅我先前的评论:所需的功能似乎在其iv
模块中不可用。
您可以通过在解密时将crypto-js
和decrypt
馈入key
来验证是否有效。但是,有一个障碍。使用盐时,iv
的方法是在输出中包含该盐,如您在此处看到的:
openssl enc
输出的前16个字节是“魔术”字节openssl
,后跟盐。如果您使用密码,该盐通常会由工具读取,但是如果您直接使用$ printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl base64 -d -A | hexdump -C
00000000 53 61 6c 74 65 64 5f 5f 9d 5a e0 6e 8a 2b 62 7c |Salted__.Z.n.+b||
00000010 7e 33 bb 56 2f fe 5e fe 1d c7 c8 a9 1f f0 c5 27 |~3.V/.^........'|
00000020
和Salted__
进行解密,则会破坏该盐。因此,在解密时,例如在使用key
时,您必须先删除该标头,然后再将字节作为密文输入到iv
中,
openssl enc
此单线代码首先进行tail
解码,然后删除前16个字节,然后将结果馈送到printf "U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=" | openssl base64 -d -A | tail -c +17 | openssl enc -d -aes-256-cbc -K 8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA -iv 4150125DCCD36F73A9F08F3020151A04
Hello
,不再需要base64
选项,因为这已经照顾好了。
在PHP中:
openssl enc
所有这些,您可能最好摆脱-base64
和$ciphertext = 'U2FsdGVkX1+dWuBuiitifH4zu1Yv/l7+HcfIqR/wxSc=';
$ciphertext_decoded = base64_decode($ciphertext);
$ciphertext_nosalt = base64_encode(substr($ciphertext_decoded, 16));
$key = hex2bin('8ACC4E30E9128FBB0763DDDA8998A7141DFDC77B9DADF0A5FC65E67E2A8313FA');
$iv = hex2bin('4150125DCCD36F73A9F08F3020151A04');
$result = openssl_decrypt($ciphertext_nosalt, 'AES-256-CBC', $key, 0, $iv);
var_dump($result);
使用的密钥派生,它依赖于OpenSSL openssl enc
函数实现的专有机制。甚至openssl enc
now warns about this being deprecated。
相反,开始使用诸如PBKDF2之类的标准算法。 crypto-js
的最新版本对此提供了支持,我已经在EVP_ByesToKey
和PHP openssl enc
模块的源代码中发现了它(但我自己从未使用过)。如果您有一个需要保留的加密数据数据库,则可以使用旧的解密方法和PKDBF2的方法一次加密其内容。确保将盐分开存储,而不是与密文一起存储为一滴。
答案 1 :(得分:0)
这里的问题是您没有使用EVP_BytesToKey
。这是用于从您的密码派生密钥和IV的OpenSSL KDF。
请注意,它是不安全的。您应该更喜欢将十六进制密钥和IV直接传递给openssl enc
。