让我们看看。 PKCS #7 在 RFC 5652(加密消息语法)中进行了描述。
填充方案本身在6.3. Content-encryption Process 部分中给出。它本质上是说:根据需要附加那么多字节来填充给定的块大小(但至少一个),并且每个字节都应该有填充长度作为值。
因此,查看最后一个解密的字节,我们就知道要删除多少字节。 (也可以检查它们是否都具有相同的值。)
我现在可以为您提供一对 PHP 函数来执行此操作,但我的 PHP 有点生疏。所以要么自己做这个(然后随时编辑我的答案以将其添加),或者查看 mcrypt 文档的user-contributed notes - 其中相当一部分是关于填充并提供 PKCS #7 填充的实现。
那么,让我们详细看看first note there:
<?php
function encrypt($str, $key)
{
$block = mcrypt_get_block_size('des', 'ecb');
这将获取所使用算法的块大小。在你的情况下,你会使用aes 或rijndael_128 而不是des,我想(我没有测试它)。 (相反,您可以简单地将16 用于 AES,而不是调用该函数。)
$pad = $block - (strlen($str) % $block);
这会计算填充大小。 strlen($str) 是数据的长度(以字节为单位),% $block 给出余数模 $block,即最后一个块中的数据字节数。 $block - ... 因此给出了填充最后一个块所需的字节数(现在这是一个介于 1 和 $block 之间的数字,包括在内)。
$str .= str_repeat(chr($pad), $pad);
str_repeat 生成一个由相同字符串的重复组成的字符串,这里重复了character given by $pad、$pad 次,即长度为$pad 的字符串,用$pad 填充。
$str .= ... 将此填充字符串附加到原始数据中。
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
这是加密本身。使用MCRYPT_RIJNDAEL_128 而不是MCRYPT_DES。
}
现在换个方向:
function decrypt($str, $key)
{
$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
解密。 (你当然会改变算法,如上所述)。 $str 现在是解密后的字符串,包括填充。
$block = mcrypt_get_block_size('des', 'ecb');
这又是块大小。 (见上文。)
$pad = ord($str[($len = strlen($str)) - 1]);
这看起来有点奇怪。最好分多步写:
$len = strlen($str);
$pad = ord($str[$len-1]);
$len 现在是填充字符串的长度,$str[$len - 1] 是该字符串的最后一个字符。 ord 将其转换为数字。因此$pad 是我们之前用作填充填充值的数字,这就是填充长度。
return substr($str, 0, strlen($str) - $pad);
所以现在我们从字符串中截断最后一个$pad 字节。 (除了strlen($str),我们也可以在这里写$len:substr($str, 0, $len - $pad)。)。
}
?>
请注意,除了使用substr($str, $len - $pad),还可以编写substr($str, -$pad),因为PHP 中的substr 函数对负操作数/参数有特殊处理,从字符串末尾开始计数。 (我不知道这是否比先获取长度并手动计算索引更有效。)
如前所述并在 rossum 的评论中指出,您应该检查它是否正确 - 即查看 substr($str, $len - $pad),并检查其所有字节是否为 chr($pad),而不是像此处所做的那样简单地剥离填充.这可以作为对损坏的轻微检查(尽管如果您使用链接模式而不是 ECB,此检查会更有效,并且不能替代真正的 MAC)。
(不过,告诉您的客户,他们应该考虑改用比 ECB 更安全的模式。)