将openssl_decrypt()与Passphase一起使用(非key / iv)

时间:2018-07-27 16:16:24

标签: php encryption openssl php-openssl

我正在尝试解密一些使用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&??

2 个答案:

答案 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省略了该步骤。结果,-nosaltkeyiv在每次运行中都将是相同的(如果使用相同的密码和明文),那么您应该能够准确地再现输出。如果您不使用ciphertext,您的实验仍然可以进行,但是每次运行的-nosaltkeyiv值将有所不同,并且您还必须摆脱掉ciphertext作为标题添加的盐中的盐-有关详细信息,请参阅此答案的进一步内容。

另一个选择是让PHP代码在调用openssl之前从密码短语中导出keyiv。为此,您将必须检查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 enckey,如下所示:

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 (以及saltkey)的选择是不同的,并且每次加密都是随机选择的行动。要直接在PHP中执行此操作,请参阅我先前的评论:所需的功能似乎在其iv模块中不可用。

您可以通过在解密时将crypto-jsdecrypt馈入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