我一辈子都想不通如何将旧的mcrypt代码迁移到OpenSSL。我在CBC的Blowfish和CBC的Rijndael都可以使用它,但是在ECB的Blowfish却让我难以理解。
是的,我读了Moving from mcrypt with Blowfish & ECB to OpenSSL,并尝试对数据进行零填充,而不是对数据进行零填充,对密钥进行零填充,对密钥进行循环以及它们的任何组合,但似乎没有任何效果。
这是我的代码:
<?php
function encrypt_with_mcrypt($data, $key) {
return mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
}
function encrypt_with_openssl($data, $key) {
return openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY);
}
$data = 'foobar';
$key = 'supersecretkey';
var_dump(base64_encode(encrypt_with_mcrypt($data, $key)));
var_dump(base64_encode(encrypt_with_openssl($data, $key)));
这是输出:
test.php:13:
string(12) "5z0q3xNnokw="
test.php:14:
string(12) "1zyqavq7sCk="
答案 0 :(得分:3)
mcrypt库/包装器默认为零字节填充(仅在需要时),而OpenSSL库/包装器默认为PKCS#5填充。这意味着单个块的填充方式不同,因此将显示不同的密文块。
一个常见的技巧是解密生成的密文而没有进行任何填充,然后通过查看十六进制的明文+填充检查填充字节。
这将向您显示
5z0q3xNnokw=
666f6f6261720000
用于mcrypt和
1zyqavq7sCk=
666f6f6261720202
用于OpenSSL。
使用较大的纯文本消息,需要对多个块进行加密,这也会向您显示,对于最后一个块,除 之外,加密进行得很好。
首先且仅当mcrypt输入不是8字节的倍数(Blowfish的块大小)时,才对数据进行零填充,然后使用OPENSSL_ZERO_PADDING
作为填充模式。
请注意,查看源代码会发现OPENSSL_ZERO_PADDING
出于某种未指定的原因似乎意味着包装程序“无填充”,而OPENSSL_NO_PADDING
似乎与其他设置冲突-我认为这是PHP OpenSSL包装器API的开发人员犯下的非常糟糕的设计和实现错误。
the great research performed by Reinier可以找到更多信息,该信息显示了API填充/未填充(或忘记填充/未填充,取决于您站立的位置)的方式。
答案 1 :(得分:2)
除了Maarten's answer之外,我没有什么要补充的,除了我认为可以显示一些说明他的单词的代码是很好的。
mcrypt
添加零以将明文填充到8字节BF块大小的倍数中,这可以通过打印明文和解密密文的十六进制转储来显示:
$key = "supersecretkey";
$data = "foobar";
$ctxt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;
给出以下十六进制转储:
666f6f626172
666f6f6261720000
openssl
默认使用PKCS#5填充,在这种情况下,在块的末尾添加2个字节,值2。
$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;
给予
666f6f626172
666f6f6261720202
可以通过手动添加填充字节来使mcrypt
和openssl
的密文保持一致。请注意OPENSSL_ZERO_PADDING
选项和"\0\0"
的附加内容:
$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt_os = openssl_encrypt($data."\0\0", 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;
给予:
e73d2adf1367a24c
e73d2adf1367a24c
或者,在使用mcrypt
时,在末尾手动插入PKCS#5填充字节:
$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data."\2\2", MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt_os = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;
给予
d73caa6afabbb029
d73caa6afabbb029
最后,尝试在禁用填充且长度不等于块大小的倍数的情况下调用openssl_encrypt()
:
$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo(openssl_error_string().PHP_EOL)
给予
error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length
备注:名称OPENSSL_ZERO_PADDING
令人困惑,但这表示“无填充”。您可能会想使用标志OPENSSL_NO_PADDING
,但是该标志不打算与openssl_encrypt()
一起使用。其值为3,与OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
相同。相反,it is intended for use with asymmetric cryptography。