使用mcrypt进行PHP解密返回无法理解的文本而不是原始明文 [英] PHP decryption with mcrypt returns unintelligible text instead of original plaintext

查看:192
本文介绍了使用mcrypt进行PHP解密返回无法理解的文本而不是原始明文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是想加密和解密一个字符串。但是当我输出解密的字符串时,我只得到:

   
^ V _ n .ZZ Ǐ : 2

我的代码: ($ string,$ secret_key =ATRTHTRAGSFRSGTS){
$ iv = mcrypt_create_iv(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB ),MCRYPT_RAND);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$ secret_key,$ string,MCRYPT_MODE_CBC,$ iv);
}

函数解密($ string,$ secret_key =ATRTHTRAGSFRSGTS){
$ iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB),MCRYPT_RAND);
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256,$ secret_key,$ string,MCRYPT_MODE_CBC,$ iv);
}
$ text ='这是一个测试'。
$ enc = encrypt($ text);
$ dec = decrypt($ enc);

echo $ dec;

任何想法可能是错的?

解决方案

您在加密期间随机生成初始化向量(IV),这意味着密文将按照IV进行随机化。当您解密时,您需要提供与加密相同的IV。



由于IV不需要秘密,您只需将其添加到密文或以其他方式发送。在解密过程中不要生成不同的IV。



如果你的明文更长(超过32个字节),你会看到前32个字节是错误的,但是其余的都是正确的。如果您想了解更多关于CBC模式的工作原理,维基百科有让您覆盖。






在加密期间,mcrypt将一个0x00字节的填充应用于明文,因为CBC模式下的Rijndael工作在16字节的块上。解密后需要删除填充,因为它不会自动完成:

  return rtrim(mcrypt_decrypt(...) ,\0); 

另外,不要忘记验证你的密文,因为否则攻击如padding-oracle攻击用于完全解密一些密文。可以使用认证模式(如GCM)或通过HMAC通过密文生成的认证标签来完成密文验证。



另见








在对 mcrypt_get_iv_size的问题的评论中有一点争议MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB)不应包含 ECB ,而是 CBC 模式,因为ECB模式不使用IV,但是mcrypt是坏的,它很快乐地为CBC返回一个有效的IV(在这种情况下为32个字节),尽管请求一个应为0字节的IV for ECB。



mcrypt是放弃的,不应该被使用。使用openssl / libsodium / defuse /...


I am just trying to encrypt and decrypt a string. But when I output the decrypted string I only get:

��
  ^����V��_��n�.ZZ��Ǐ��:�2��

My code:

function encrypt($string, $secret_key = "ATRTHTRAGSFRSGTS") {                                                                                                                   
   $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);                                                                      
   return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $secret_key, $string, MCRYPT_MODE_CBC, $iv);                                                                             
}                                                                                                                                                                       

function decrypt($string, $secret_key = "ATRTHTRAGSFRSGTS") {                                                                                                                   
   $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);                                                                      
   return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $secret_key, $string, MCRYPT_MODE_CBC, $iv);                                                                             
}                                                                                                                                                                       
$text = 'This is a test.';                                                                                                                                              
$enc = encrypt($text);                                                                                                                                                  
$dec = decrypt($enc);                                                                                                                                                   

echo $dec;

Any idead what could be wrong?

解决方案

You're randomly generating the initialization vector (IV) during encryption which means that the ciphertext will be randomized according to that IV. When you decrypt, you need to provide the same IV that you used for encryption.

Since the IV doesn't need to be secret, you can simply prepend it to the ciphertext or send it along in some other way. Don't generate a different IV during decryption.

If your plaintext were longer (more than 32 bytes), you would have seen that the first 32 bytes were wrong, but the rest were correct. If you want to know more about how CBC mode works, Wikipedia has got you covered.


During encryption, mcrypt applies a padding of 0x00 bytes to the plaintext, because Rijndael in CBC mode works on blocks of 16 bytes. You need to remove the padding after decryption, because it isn't done automatically:

return rtrim(mcrypt_decrypt(...), "\0");

Also, don't forget to authenticate your ciphertexts, because otherwise attacks such as padding-oracle attack can be used to completely decrypt some ciphertext. Ciphertext authentication can be done with an authenticated mode like GCM or with an authentication tag generated through an HMAC pass over the ciphertext.

See also


There was a little controversy in the comments to the question that mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB) should not contain ECB, but rather CBC mode, because ECB mode doesn't use an IV, but mcrypt is bad and it happily returns a valid IV for CBC (32 byte in this case) despite requesting an "IV for ECB" which should have been 0 byte.

mcrypt is abandonware and should not be used anymore. Use openssl/libsodium/defuse/...

这篇关于使用mcrypt进行PHP解密返回无法理解的文本而不是原始明文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆