3

考虑以下 PHP 代码:

<?php
$key = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
                            $key,
                            $data,
                            MCRYPT_MODE_CBC,
                            $iv);

print "Encoded1: " . base64_encode($encrypted) . "\n";

$key = "12345678123456781234567812345678";

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
                            $key,
                            $data,
                            MCRYPT_MODE_CBC,
                            $iv);

print "Encoded2: " . base64_encode($encrypted) . "\n";

运行时,这会产生输出:

Encoded1: iz1qFlQJfs6Ycp+gcc2z4w==
Encoded2: n3D26h/m8CSH0CE+z6okkw==

请注意,我从PHP Java AES CBC Encryption Different Results中窃取了第一段代码

现在 - 这是问题:

在第一种情况下,传入的密钥是一个 16 个字符的字符串。如果每个单独的字符都被解释为一个 8 位的数量,这将给出预期的 128 位密钥大小。事实上,我在上面引用的 StackOverflow 页面上的 Java 代码正是这样做的,并获得了与 PHP 相同的结果。

在上面的第二次调用中mcrypt_encrypt,我将密钥的长度加倍。 mcrypt_encrypt很高兴地接受了这一点,但产生了与第一种情况不同的加密输出。因此,很明显,它认为这是一个不同的密钥——例如,它不会只取前 128 位并丢弃任何过去的位。

那么,如何mcrypt_encrypt处理输入的密钥字符串以得出MCRYPT_RIJNDAEL_128算法所需的 128 位密钥呢?

如果它有任何区别,我特别感兴趣的情况是像我的第二个示例一样传入 32 个字符的字符串 - 我必须创建一个匹配的解密例程(在 Java 中),所以我需要弄清楚如何在这种情况下实际上生成了密钥。我引用的页面有非常好的 Java 代码(它适用于我所有的测试用例)——我只是缺少正确的关键字节集。

4

1 回答 1

3

Rijndael 算法有两个重要参数。有密钥大小(128 位、192 位和 256 位),然后有块大小(128 位、192 位和 256 位)。128inMCRYPT_RIJNDAEL_128指的是块大小。密钥大小是可变的。

当你将不同长度的密钥传递给 MCrypt 时,它会自动选择合适的密钥大小,所以你不要也不能设置它。MCRYPT_RIJNDAEL_128是 AES(AES-128、AES-192、AES-256)。MCRYPT_RIJNDAEL_192并且MCRYPT_RIJNDAEL_256不再是 AES。

如果 Java 代码为 128 位密钥生成匹配结果,那么它也会为 256 位密钥生成匹配结果。

MCrypt 有点奇怪。在 PHP 5.6.0 版本之前,它可以采用任何密钥长度,而不仅仅是 128 位、192 位或 256 位。密钥将填充 0x00 字节,直到下一个有效密钥长度。


由于 Java 不支持开箱即用的 ZeroPadding,因此您应该使用适当的填充方案,例如 PHP 中的 PKCS#5/PKCS#7 填充。这个答案有一个很好的实现。

于 2015-06-09T22:20:08.233 回答