考虑以下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位密钥大小.确实,我上面引用的*页面上的Java代码可以做到这一点,并获得与PHP相同的结果.
在上面对mcrypt_encrypt的第二次调用中,我将密钥的长度加倍. mcrypt_encrypt很高兴地接受这一点,但是产生的加密输出与第一种情况不同.因此,显然,它认为这是一个不同的密钥-例如,它不会仅占用前128位,而不会丢弃任何旧的密钥.
那么,mcrypt_encrypt如何处理输入密钥字符串以得出MCRYPT_RIJNDAEL_128算法所需的128位密钥?
如果有什么不同,我特别感兴趣的是像第二个示例一样传入32个字符的字符串-我必须创建一个匹配的解密例程(在Java中),因此我需要弄清楚在这种情况下实际上会生成密钥.我引用的页面具有完美的Java代码(可用于所有测试用例)-我只是缺少适当的一组关键字节.
解决方法:
Rijndael算法有两个重要参数.有密钥大小(128位,192位和256位),然后有块大小(128位,192位和256位). MCRYPT_RIJNDAEL_128中的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不支持开箱即用,因此您应该使用适当的填充方案,例如PHP中的PKCS#5 / PKCS#7填充. This answer具有非常好的实现.