我想我应该先处理我的第一个陈述。当您使用 Open SSL 库时,PHP 进行 AES-128-CBC 加密的方式是否存在不一致?
我问这个是因为如果您查看RFC3602的 AES,那么您会在第 4 节中找到一些测试向量。我尝试的第一个是:
案例 #1:使用 AES-CBC 和 128 位密钥加密 16 个字节(1 个块)
密钥:0x06a9214036b8a15b512e03d534120006
四:0x3dafba429d9eb430b422da802c9fac41
Paintext:“单块味精”
密文:0xe353779c1079aeb82708942dbe77181a
使用带有以下代码的开放 SSL 库:
echo bin2hex(openssl_encrypt($str, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv));
你实际上回来了:
e353779c1079aeb82708942dbe77181ab97c825e1c785146542d396941bce55d
现在这一切都很好,并且可以很好地解密,前 32 个字节是预期的密文“e353779c1079aeb82708942dbe77181a”。但是,从 Open SSL 库返回的密文末尾似乎还有另外 32 个字节。为什么是这样?这个问题接下来变得相关。
作为我正在做的事情的一部分,我正在尝试从头开始实施 AES CBC 模式加密。我目前用于执行此操作的代码是(基于先前引用的 RFC 和“块密码操作模式”维基百科页面上的 CBC 图):
public function cbcEncrypt($str, $iv, $key) {
$bl = 16;
$bs = str_split($str,$bl);
$pv = null;
$r = '';
foreach($bs as $b) {
if($pv === null) $pv = $iv;
if(strlen($b)<$bl) $b = $this->pkcs7Pad($b, $bl);
$pv = openssl_encrypt($b^$pv, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
$r .= $pv;
}
return $r;
}
目前我很确定这段代码无论如何都不会在比当前测试的 16 字节字符串长的字符串上工作,但是,我将这段代码与 Open SSL 实现并排运行以进行比较。CBC 实现的返回值是:
e353779c1079aeb82708942dbe77181a8b1ccc6f8cd525ffe22d6327d891a063
通过以下方式调用时:
$crypto = new CryptoTools();
$enc = $crypto->cbcEncrypt('Single block msg',hex2bin("3dafba429d9eb430b422da802c9fac41"),hex2bin("06a9214036b8a15b512e03d534120006"));
同样,前 32 个字节是正确的,但再次添加了后 32 个字节,但与 CBC 的 Open SSL 实现完全不同。关于为什么的任何线索?
此外,ECB 模式的 Open SSL 实现返回一个 32 字节字符串,这意味着 CBC 加密的字符串当前长度大于 16 字节,$pv 的新值将是 32 字节长度,然后它将与第二个字节进行异或纯文本块实际上不应该 $pv 总是 16 字节的长度?通常只是接受你将 $pv 从 32 字节截断到 16 字节吗?
以防万一,上面代码中的 pkcs7Pad 方法如下所示:
public function pkcs7Pad($str, $b = 8) {
if(trim($str) == '') return false;
if((int)$b < 1) return false;
$p = $b - (strlen($str) % $b);
return $str . str_repeat(chr($p),$p);
}
任何帮助将不胜感激。不用说这方面的文档不多,因为人们通常不会愚蠢到尝试重新发明轮子,当然是在 PHP 中......