常见的PHP mcrypt_encrypt()代码有时会无法解密

时间:2015-01-16 05:36:11

标签: php encryption

此代码来自多个网站,其来源提到SO作为其创建帮助的来源。 这是未压缩的版本,我将base64编码和解码更改为hex2bin和bin2hex,因为我认为这在网页之间传递时给我带来了问题,但是因为我在使用垃圾数据作为解密结果。

对于任一代码,使用相同的密钥和不同的数据,有时会将垃圾数据作为解密结果。它似乎是数据本身,而不是重复,数据大小可能在30到80个字符之间。

数据只是一系列数字和字母,都在通常的ASCII标准范围内,没有空值,没有超出标准电子邮件地址字符。

我正在使用Ubuntu 14.04 LTS,PHP 5.5.9-1ubuntu4.5(Zend:2.5.0)

有没有人遇到过这种问题?

编辑:从阅读更多的SO,显然$ iv对于加密和解密时应该是相同的,但如果这是真的,为什么它大部分时间都可以工作?

function mc_encrypt($encrypt, $mc_key) {
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
    $passcrypt = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $mc_key, trim($encrypt), MCRYPT_MODE_ECB, $iv));
    $encode = bin2hex($passcrypt);
    return $encode;
}

function mc_decrypt($decrypt, $mc_key) {
    $decoded = hex2bin($decrypt);
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
    $decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $mc_key, trim($decoded), MCRYPT_MODE_ECB, $iv));
    return $decrypted;
}

1 个答案:

答案 0 :(得分:1)

Rijndael是一种对称分组密码。它在块上运行,在这种情况下,块是256位宽。密文应该看起来是随机的,但事实并非如此。否则,将无法撤消加密。由于这是ECB模式,因此没有IV随机化整个事物。因此,与密钥组合的每个明文块将产生相同的密文块。因此,该问题只能基于相同的数据重现。

由于加密应该看起来是随机的,因此密文的第一个字节将包含一个空格(当显示为字符时)。 trim删除了6种类型的空格,并且在这些情况下至少会删除第一个字节。因此,问题将发生在6/256个案例中(约2%)。

例如,如果第一个字节是空格,那么它将被删除。现在第一个块没有所有字节,并且移动了用于解密的块边界。因此,来自下一个块的第一个字节用作第一个块的最后一个字节来解密块。由于Rijndael是分组密码,因此只会出现垃圾。这种情况发生在所有街区。

通常使用rtrim代替trim来解压缩明文: rtrim(mcrypt_decrypt(...), '\0');。应完全删除密文的trim

这不是全文,因为加密/解密的结束被修剪,这可能会破坏结果。最好实际使用PKCS#7 padding by implementing it yourself并完全失去*trim。可悲的是,php_mcrypt没有提供PKCS#7 / PKCS#5填充。