常见的PHPmcrypt_encrypt()代码有时会解密失败

Common PHP mcrypt_encrypt() code sometimes fails decryption

此代码来自多个网站,其来源提到了 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;
}

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

由于加密看起来是随机的,因此密文的第一个字节会包含白色space(当显示为字符时)。 trim 删除 6 种类型的白色 space 并且在这些情况下将至少删除第一个字节。所以,这个问题会在 6/256 的情况下发生 (~2%)。

如果第一个字节是 space,那么它将被删除。现在第一个块没有所有字节,并且移动了用于解密的块边界。因此,下一个块的第一个字节用作第一个块的最后一个字节来解密该块。由于 Rijndael 是块密码,因此只会产生垃圾。这发生在所有块上。

通常使用rtrim代替trim去填充明文:rtrim(mcrypt_decrypt(...), '[=13=]');。密文的trim要全部去掉

这不是完整的故事,因为 encryption/decryption 的末尾被修剪,这可能会破坏结果。最好实际使用PKCS#7 padding by implementing it yourself,完全丢掉*trim。遗憾的是 php_mcrypt 不提供 PKCS#7/PKCS#5 填充。