为了账号安全,请及时绑定邮箱和手机立即绑定

如何从AES加密字符串中添加/删除PKCS 7填充?

/ 猿问

如何从AES加密字符串中添加/删除PKCS 7填充?

叮当猫咪 2019-07-15 19:27:37

如何从AES加密字符串中添加/删除PKCS 7填充?

我试图使用128位AES加密(ECB)加密/解密字符串。我想知道的是如何添加/删除PKCS 7填充。Mcrypt扩展似乎可以处理加密/解密,但填充必须手动添加/删除。

有什么想法吗?


查看完整描述

3 回答

?
慕姐4208626

让我们看看。PKcs#7在RFC 5652(加密消息语法)中进行了描述。

填充方案本身在章节中给出。6.3.内容加密过程..它本质上是这样说的:根据需要追加许多字节来填充给定的块大小(但至少有一个字节),并且每个字节都应该以填充长度作为值。

因此,看看最后一个解密的字节,我们知道要去掉多少字节。(还可以检查它们是否都具有相同的值。)

现在我可以为您提供一对PHP函数,但是我的PHP有点生疏。所以要么自己做这件事(然后随意编辑我的答案来添加它),要么看一下用户投稿说明对于mcrypt文档,其中相当一部分是关于填充,并提供了PKCS#7填充的实现。


所以,让我们看看第一个注意事项详细情况:

<?phpfunction encrypt($str, $key)
 {
     $block = mcrypt_get_block_size('des', 'ecb');

这将获得所使用算法的块大小。在你的例子中,你会用aesrijndael_128而不是des,我想(我没有测试)。(相反,你可以简单地16这里是AES,而不是调用函数。)

     $pad = $block - (strlen($str) % $block);

这将计算填充的大小。strlen($str)是数据的长度(以字节为单位),% $block给出剩余模$block,即最后一个块中的数据字节数。$block - ...因此,给出了填充最后一个块所需的字节数(这是一个介于1$block,包括在内)。

     $str .= str_repeat(chr($pad), $pad);

str_repeat生成一个由相同字符串的重复组成的字符串,这里是由.赋予的特征 $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),作为substrPHP中的函数对负操作数/参数有一个特殊的处理,可以从字符串的末尾计数。(我不知道这是否比先获取长度和手工计算索引更有效。)

如前所述,并在Rossum的评论中指出,与其像这里那样简单地去掉填充物,不如检查它是否正确-即看看substr($str, $len - $pad),并检查其所有字节是否为chr($pad)..这是一种防止腐败的轻微检查(尽管如果您使用链接模式而不是欧洲央行,而不是替代真正的MAC,则此检查更有效)。


(不过,告诉你的客户,他们应该考虑换一种比欧洲央行更安全的模式。)


查看完整回答
反对 回复 2019-07-15
?
春华秋衣

我创建了两个方法来执行填充和取消填充。使用phpdoc需要PHP 5。正如您注意到的,unpad函数包含大量异常处理,为每个可能的错误生成不少于4条不同的消息。

要获得PHP mcrypt的块大小,可以使用mcrypt_get_block_size,它还将块大小定义为字节而不是位。

/**
 * Right-pads the data string with 1 to n bytes according to PKCS#7,
 * where n is the block size.
 * The size of the result is x times n, where x is at least 1.
 * 
 * The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3.
 * This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES.
 *
 * @param string $plaintext the plaintext encoded as a string containing bytes
 * @param integer $blocksize the block size of the cipher in bytes
 * @return string the padded plaintext
 */function pkcs7pad($plaintext, $blocksize){
    $padsize = $blocksize - (strlen($plaintext) % $blocksize);
    return $plaintext . str_repeat(chr($padsize), $padsize);}/**
 * Validates and unpads the padded plaintext according to PKCS#7.
 * The resulting plaintext will be 1 to n bytes smaller depending on the amount of padding,
 * where n is the block size.
 *
 * The user is required to make sure that plaintext and padding oracles do not apply,
 * for instance by providing integrity and authenticity to the IV and ciphertext using a HMAC.
 *
 * Note that errors during uppadding may occur if the integrity of the ciphertext
 * is not validated or if the key is incorrect. A wrong key, IV or ciphertext may all
 * lead to errors within this method.
 *
 * The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3.
 * This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES.
 *
 * @param string padded the padded plaintext encoded as a string containing bytes
 * @param integer $blocksize the block size of the cipher in bytes
 * @return string the unpadded plaintext
 * @throws Exception if the unpadding failed
 */function pkcs7unpad($padded, $blocksize){
    $l = strlen($padded);

    if ($l % $blocksize != 0) 
    {
        throw new Exception("Padded plaintext cannot be divided by the block size");
    }

    $padsize = ord($padded[$l - 1]);

    if ($padsize === 0)
    {
        throw new Exception("Zero padding found instead of PKCS#7 padding");
    }    

    if ($padsize > $blocksize)
    {
        throw new Exception("Incorrect amount of PKCS#7 padding for blocksize");
    }

    // check the correctness of the padding bytes by counting the occurance
    $padding = substr($padded, -1 * $padsize);
    if (substr_count($padding, chr($padsize)) != $padsize)
    {
        throw new Exception("Invalid PKCS#7 padding encountered");
    }

    return substr($padded, 0, $l - $padsize);}

这在任何方面都不会使Paŭlo Ebermann的答案失效,它基本上是代码&phpdoc中的相同答案,而不是描述。


请注意,向攻击者返回填充错误可能会导致填充甲骨文攻击它完全破坏了CBC(当使用CBC代替欧洲央行或安全认证密码时)。


查看完整回答
反对 回复 2019-07-15
?
噜噜哒

只需在解密数据后调用以下函数

function removePadding($decryptedText){
    $strPad = ord($decryptedText[strlen($decryptedText)-1]);
    $decryptedText= substr($decryptedText, 0, -$strPad);
    return $decryptedText;}


查看完整回答
反对 回复 2019-07-15

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信