找出PHP的mcrypt创建的确切密钥

12
我维护的一个PHP应用程序使用Rijndael_256和EBC_MODE加密mcrypt。奇怪的是,密钥长度不是256位,而只有160位。根据mcrypt_encrypt文档,如果密钥太小,将使用\ 0进行填充以获取所需的大小。
  

将用于加密数据的密钥。如果它比所需的键大小要小,则会被填充为'\ 0'。最好不要使用ASCII字符串作为密钥。

这似乎发生在mcrypt.c文件的开始处(大约在第1186行),并在第1213行修改密钥。
所以假设我们有 $key ='abcdefghijkm'; ,它太短了,但是PHP的mcrypt实现确保在使用RIJNDAEL_256时将其扩展到32个字符(或256位)。最终密钥会是什么样子?
我问这个问题是因为另一个应用程序正在构建,该应用程序使用相同的加密数据,但是使用另一种语言。确切地说是使用Perl,我正在使用Crypto::Rijndael。对于给定的示例密钥,我需要在Crypto::Rijndael(或任何其他密钥)中输入什么才能再次解密数据? 更新

使用Perl可以通过pack('a32', 'my secret key'); (或者使用Z32)生成以\0为填充的密钥,length()函数将返回32,这个密钥可以被Crypt::Rijndael模块所接受。查看PHP的mcrypt源代码,应该是生成了相同的以\0填充的密钥,但实际上并不行。

理论上,在PHP中使用pack('a32', 'my secret key');应该会得到与PHP的mcrypt生成的相同的以\0填充的密钥,但事实并非如此。

我已经接近放弃,准备用新密钥重新加密所有内容。这太费时间了。


你尝试过用0填充它吗? - MrGlass
是的,不过我在这方面没有什么好运。 - Htbaa
请记住,结果可能实际上不是可打印字符,因此不能依赖控制台/页面输出来确定键的实际样子。但如果将生成的密钥写入二进制文件,您就应该能够将其引入您的Perl脚本并成功地使用它。 - WWW
我正在尝试使用PHP的mcrypt_encrypt加密的纯文本,并尝试使用Perl的Crypt::Rijndael进行解密。我只需要知道当PHP的mcrypt_encrypt完成后实际密钥的样子是什么:-)。 - Htbaa
不好意思没有直接回答问题,但我觉得这是个不好的主意。如果您的密钥是密码,请先使用 KDF(例如 PBKDF2、bcrypt、scrypt)延展您的值,然后使用结果加密数据。 - Chris Smith
您能解释一下 KDF 是什么意思吗?这里加密的数据类型并不重要。事实上,我必须处理以这种方式加密的数据,并且需要使用另一种语言(在这种情况下是 Perl)对其进行解密。 - Htbaa
2个回答

12
问题不在密钥的填充上,而是因为您正在使用两种不同的块大小。在PHP中,使用" MCRYPT_RIJNDAEL_256 "将使用256位的块大小。然而,在Perl中使用"Crypt::Rijndael"时,他们指出:块大小对于Rijndael来说是16字节(128位),尽管算法实际上支持任何倍数的字节的块大小。但是,128位是AES规定的块大小,所以这是我们所支持的。因此没有一个密钥可以实现这两个不同算法之间的转换。您可以在PHP中切换到128位。
<?
$key = "abcdefghijklmnopqrstuvwxyz";
$data = "Meet me at 11 o'clock behind the monument.";
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB, nil);
echo bin2hex($crypttext) . "\n";
// prints c613d1804f52f535cb4740242270b1bcbf85151ce4c874848fd1fc2add06e0cc2d26b6403feef4a8df18f7dd7f8ac67d
?>

哪个Perl版本可以使用Crypt::Rijndael无问题地解密:

use Crypt::Rijndael;
$key = "abcdefghijklmnopqrstuvwxyz\0\0\0\0\0\0";
$crypttext = "c613d1804f52f535cb4740242270b1bcbf85151ce4c874848fd1fc2add06e0cc2d26b6403feef4a8df18f7dd7f8ac67d";
$cipher = Crypt::Rijndael->new($key, Crypt::Rijndael::MODE_ECB());
print $cipher->decrypt(pack('H*', $crypttext));
# prints "Meet me at 11 o'clock behind the monument."

或者你可以切换到另一个支持更多块大小的Perl模块,例如 Crypt::Rijndael_PP

# Same PHP code except using MCRYPT_RIJNDAEL_256
# prints f38469ec9deaadbbf49bb25fd7fc8b76462ebfbcf149a667306c8d1c033232322ee5b83fa87d49e4e927437647dbf7193e6d734242d583157b492347a2b1514c

Perl:

->

Perl编程语言:

use Crypt::Rijndael_PP ':all';
$key = "abcdefghijklmnopqrstuvwxyz\0\0\0\0\0\0";
$crypttext = "f38469ec9deaadbbf49bb25fd7fc8b76462ebfbcf149a667306c8d1c033232322ee5b83fa87d49e4e927437647dbf7193e6d734242d583157b492347a2b1514c";
print rijndael_decrypt(unpack('H*', $key), MODE_ECB, pack('H*', $crypttext), 256, 256);
# prints "Meet me at 11 o'clock behind the monument."

谢谢,我已经开始考虑块大小是否与此有关,但还没有时间去检查它。我想我会选择 Crypt::Rijndael_PP - Htbaa
嗯,虽然$key实际上有256位长,但Crypt::Rijndael_PP仍会在其后添加0。看起来像是它有一个错误? - Htbaa
@Htbaa Crypt::Rijndael_PP 模块不如 Crypt::Rijndael 成熟,并且存在一些奇怪的问题。特别是,它不需要原始密钥,而是需要十六进制字符串表示。请参见我使用它的示例:您必须在传递之前对原始密钥进行 unpack - blahdiblah
啊,我明白了,确实有点奇怪。我会试一下的。 - Htbaa

4

'\0'代表NULL,它的十六进制值为00!所以我测试了三个代码,它们都返回相同的结果 :)

  1. 让mcrypt_encrypt进行'\0'填充
  2. 添加PHP NULL值
  3. 添加PHP转换的0十六进制值

Code:

function encryptThis($text,$key){
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
    return ($crypttext);
}

echo bin2hex(encryptThis("Meet me at 11 o'clock behind the monument.", "abcdefghijklmnopqrstuvwxyz"))."<br/>";

echo bin2hex(encryptThis("Meet me at 11 o'clock behind the monument.", "abcdefghijklmnopqrstuvwxyz" . NULL . NULL . NULL . NULL . NULL . NULL))."<br/>";

echo bin2hex(encryptThis("Meet me at 11 o'clock behind the monument.", "abcdefghijklmnopqrstuvwxyz" . hex2bin(0) . hex2bin(0) . hex2bin(0) . hex2bin(0) . hex2bin(0) . hex2bin(0)))."<br/>";

?>


我很满意让 mcrypt_encrypt 做 \0 填充。我需要确切的密钥,以便在另一种编程语言中使用它。感谢提到 0x00,我已经忘记了这个。不知道 Perl 的 undef 是否相同(我怀疑不会)。 - Htbaa
必须是32个字符。在这个例子中,字母表有26个字母,32-26 = 6,所以你必须添加6个Nulls或0x00 :) - HamZa
格式现在混乱了。实际上,$key1和$key2的长度完全相同,所以mcrypt_encrypt仍然负责填充。$key3也是如此。因此,PHP认为它们都包含“abcdefghijklmnopqrstuvwxyz”,它们给出相同的结果,这就不足为奇了。 - Htbaa
尝试在生成的密钥上运行 strlen()。它将对所有密钥报告26,这根据你所说的不应该是正确的。 - Htbaa
1
由于 strlen() 函数计算字符串的字节数,因此我期望它也会计算 \0,因为这也是一个字节。由于我们还不确定 PHP 在 mcrypt 修改密钥字符串之前对字符串做了什么,所以仍然无法确定字符串最终应该是什么样子。 - Htbaa
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接