为啥 mcrypt_encrypt() 将二进制字符放在我的字符串末尾?

Posted

技术标签:

【中文标题】为啥 mcrypt_encrypt() 将二进制字符放在我的字符串末尾?【英文标题】:Why is mcrypt_encrypt() putting binary characters at the end of my string?为什么 mcrypt_encrypt() 将二进制字符放在我的字符串末尾? 【发布时间】:2012-04-04 14:58:06 【问题描述】:

这是一个加密和解密数据的php演示脚本:

<?

$encryptionkey = 'h8y2p9d1';

$card_nbr = "1234";
echo "original card_nbr: $card_nbr <br>\n";

$card_nbr_encrypted=encrypt_data($card_nbr);
echo "card_nbr_encrypted: $card_nbr_encrypted <br>\n";

$card_nbr_decrypted=decrypt_data($card_nbr_encrypted);
echo "card_nbr_decrypted: $card_nbr_decrypted <br>\n";

$len=strlen($card_nbr_decrypted);
echo "length: $len <br>\n";



function encrypt_data($text)
  global $encryptionkey;
  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
  $encrypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $encryptionkey, $text, MCRYPT_MODE_ECB, $iv);
  return $encrypted_text;


function decrypt_data($text)
  global $encryptionkey;
  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
  $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $encryptionkey, $text, MCRYPT_MODE_ECB, $iv);
  return $decrypted_text;


?>

输出是:

original card_nbr: 1234
card_nbr_encrypted: vY¨(Z$<§G3-žÃ-Éù3Ý2Ê×rz¨VÛ
card_nbr_decrypted: 1234  (and 28 binary characters)
length: 32 

输出解密成功,但末尾添加了 28 个二进制字符。在 Firefox 中查看 html 源代码时,最容易看到这一点。 字符串长度 32 也证明了这一点。有什么想法吗?

【问题讨论】:

看看这是否有任何帮助php.net/manual/en/function.base64-encode.php @Mian_Khurram_Ijaz 我不明白为什么会有帮助 输出实际上是二进制格式,因此为了确保跨网络安全,base64_encode 有助于加密和解密工作正常,所以我认为如果二进制字符是问题,然后使用 base64.. 我读到 ECB 模式有缺陷,建议使用 CBC 或 CBF。看看为什么在这里有一个有效的实现:slideshare.net/ircmaxell/cryptography-for-the-average-developer(我自己也是新手) 【参考方案1】:

使用空字符 \0 填充返回的字符串以填充 n * blocksize 字节,这就是您看到额外数据的原因。

如果您运行$card_nbr_decrypted= rtrim($card_nbr_decrypted, "\0");,它应该会返回实际数据。

【讨论】:

如果您想加密实际上可能以一定数量的空值结尾的任意二进制数据,使用rtrim 的解决方案当然会被破坏。在这种情况下,您应该将字符串的长度带外传输并将substr 传输回该长度(很容易,但会泄漏字符串的长度),或者在加密之前自己进行填充 - 我建议使用PKCS#7 填充方法,因为它很容易理解。 PKCS#7 的问题在于编写和维护 mcrypt 的 bozos 确实为它提供了支持。【参考方案2】:

好像是known problem。解码后使用rtrim()去除多余的NUL。

【讨论】:

【参考方案3】:

您收到空字节是因为您正在为您的block cipher mode of operation 使用电子代码块 (ECB),它会填充纯文本的末尾以适应块大小。在您的情况下,块大小为 256 位,因为您使用的是 MCRYPT_RIJNDAEL_256

如果您使用 密码反馈 (CFB) 模式 — MCRYPT_MODE_CFB — 没有空字节,无需修剪,您可以一起避免这个填充问题。但是,对于 CFB,您应该 HMAC 您的加密数据,以验证它没有被 (see "Mallet") 篡改。您可以在 Cryptography For The Average Developer 找到一个工作实现的示例。

另外值得注意的是,ECB 模式被认为不太安全,因为它可以reveal data patterns。此外,ECB(以及 CBC,因为它也有 pad)可能容易受到 padding oracle attack 的攻击。

【讨论】:

【参考方案4】:

我认为问题在于您在以下情况下使用二进制数据:

mcrypt_encrypt — 使用给定参数加密明文

您可以使用 base64_encode($text) 来使用纯文本。

【讨论】:

编码和加密不是一回事。 我知道。但是如果你使用 mcrypt_encrypt 将 $text 作为 base64 明文发送以避免编码问题很有用。您可以按照其他用户的建议使用 base64_encode($text) 或 rtrim()。

以上是关于为啥 mcrypt_encrypt() 将二进制字符放在我的字符串末尾?的主要内容,如果未能解决你的问题,请参考以下文章

python为啥中文显示的都是16进制的?

什么是最安全的 mcrypt_encrypt 算法?

JAVA中,使用字节流读写文件,为啥找不到文件的位置?请看补充:

在 PHP 中将 mcrypt_encrypt 转换为 openssl_encrypt

PHP 7.2中mcrypt_encrypt的精确替代

致命错误:调用未定义的函数 mcrypt_encrypt [重复]