如何添加/删除AESencryptionstring的PKCS7填充?
我试图使用128位AESencryption(ECB)来encryption/解密一个string。 我想知道的是我可以如何添加/删除PKCS7填充。 似乎Mcrypt扩展可以照顾encryption/解密,但填充必须手动添加/删除。
有任何想法吗?
让我们来看看。 在RFC 5652(encryption消息语法)中描述了PKCS#7。
填充scheme本身在6.3节给出。 内容encryption过程 。 它基本上是这样说的:根据需要添加多个字节来填充给定的块大小(但至less有一个),并且它们中的每一个都应该具有作为值的填充长度。
因此,看最后一个解密的字节,我们知道有多less字节去掉。 (人们也可以检查他们都有相同的价值。)
我现在可以给你一个PHP函数来做到这一点,但我的PHP有点生疏。 所以要么自己做(然后随意编辑我的答案来添加它),或者看看用户提供的mcrypt文档的注释 – 其中一些是关于填充和提供PKCS#7填充的实现。
所以,我们来仔细看看第一个注意事项 :
<?php function encrypt($str, $key) { $block = mcrypt_get_block_size('des', 'ecb');
这得到了使用algorithm的块大小。 在你的情况下,你会使用aes
或rijndael_128
而不是des
,我想(我没有testing它)。 (相反,您可以简单地在这里input16
,而不是调用该函数。)
$pad = $block - (strlen($str) % $block);
这将计算填充大小。 strlen($str)
是数据的长度(以字节为单位), % $block
给出了剩余的模$block
,即最后一个块中的数据字节数。 $block - ...
因此给出了填充最后一个块所需要的字节数(现在是一个介于1
和$block
之间的数字)。
$str .= str_repeat(chr($pad), $pad);
str_repeat
产生一个由同一个string重复组成的string,这里是$pad
, $pad
times 给出的字符的重复,即一个长度$pad
的string,用$pad
。 $str .= ...
将此填充string附加到原始数据。
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
这是encryption本身。 使用MCRYPT_RIJNDAEL_128
而不是MCRYPT_DES
。
}
现在另一个方向:
function decrypt($str, $key) { $str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
解密。 (你当然会改变algorithm,如上)。 $ str现在是解密的string,包括填充。
$block = mcrypt_get_block_size('des', 'ecb');
这又是块的大小。 (往上看。)
$pad = ord($str[($len = strlen($str)) - 1]);
这看起来有点奇怪 最好多写几步:
$len = strlen($str); $pad = ord($str[$len-1]);
$len
现在是填充string的长度,而$str[$len - 1]
是这个string的最后一个字符。 ord
将其转换为数字。 因此, $pad
是我们以前用作填充的填充值的数字,这是填充长度。
return substr($str, 0, strlen($str) - $pad);
所以现在我们从string中$pad
最后的$pad
字节。 (而不是strlen($str)
我们也可以在这里写$len
: substr($str, 0, $len - $pad)
)。
} ?>
请注意,不是使用substr($str, $len - $pad)
,也可以编写substr($str, -$pad)
,因为PHP中的substr
函数对负操作数/参数进行了特殊处理,从string的末尾开始。 (我不知道这是否比首先获取长度和手动计算索引效率更高或更低。)
正如之前所说的,在rossum的评论中指出,不是简单地剥离像这里所做的填充,而是检查它是正确的 – 即看看substr($str, $len - $pad)
,并检查它的全部字节是chr($pad)
。 这是对腐败的轻微检查(尽pipe如果您使用链接模式而不是ECB,并且不是实际MAC的替代品,则此检查更为有效)。
(而且,告诉你的客户,他们应该考虑改用比ECB更安全的模式。)
我已经创build了两个方法来执行填充和取消填充。 这些函数使用phpdoc
进行logging,并且需要PHP 5.如您将注意到,unpad函数包含大量exception处理,为每个可能的错误生成不less于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ŭloEbermann的答案无效,它在代码&phpdoc中的基本相同的答案而不是描述。
请注意,向攻击者返回填充错误可能会导致填充oracle攻击 ,从而完全破坏CBC(当使用CBC而不是ECB或安全authentication密码时)。
- Java默认的Crypto / AES行为
- 为什么我的AESencryption会抛出一个InvalidKeyException?
- InvalidKeyException非法密钥大小
- 如何使用Python / PyCrypto以OpenSSL兼容的方式对文件进行AESencryption/解密?
- 如何避免在部署应用程序时安装“无限强度”JCE策略文件?
- Java 256位AES基于密码的encryption
- 如何使用AES解密用openssl命令encryption的Java文件?
- 如何selectAESencryption模式(CBC ECB CTRB OCB CFB)?
- 在C#中使用AESencryption