PHP的AESencryption/解密

我find了一个在PHP中使用/解码string的例子。 起初它看起来非常好,但它不会工作:-(

有谁知道问题是什么?

$Pass = "Passwort"; $Clear = "Klartext"; $crypted = fnEncrypt($Clear, $Pass); echo "Encrypted: ".$crypted."</br>"; $newClear = fnDecrypt($crypted, $Pass); echo "Decrypted: ".$newClear."</br>"; function fnEncrypt($sValue, $sSecretKey) { return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sDecrypted, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); } function fnDecrypt($sValue, $sSecretKey) { return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sEncrypted), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))); } 

结果是:

encryption: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

解密: —‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—

$sDecrypted$sEncrypted在您的代码中未定义。 看到有效的解决scheme( 但不安全! ):


停!

这个例子是不安全的! 不要使用它!


 $Pass = "Passwort"; $Clear = "Klartext"; $crypted = fnEncrypt($Clear, $Pass); echo "Encrypred: ".$crypted."</br>"; $newClear = fnDecrypt($crypted, $Pass); echo "Decrypred: ".$newClear."</br>"; function fnEncrypt($sValue, $sSecretKey) { return rtrim( base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_ECB, mcrypt_create_iv( mcrypt_get_iv_size( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB ), MCRYPT_RAND) ) ), "\0" ); } function fnDecrypt($sValue, $sSecretKey) { return rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_ECB, mcrypt_create_iv( mcrypt_get_iv_size( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB ), MCRYPT_RAND ) ), "\0" ); } 

但是在这个代码中还有其他的问题使得它不安全,尤其是使用ECB(这不是一种encryption模式,只能在其上定义encryption模式的构build块)。 请参阅Fab Sa的答案,以便及时解决最糟糕的问题,以及Scott如何做到这一点的答案 。

请使用现有的安全PHPencryption库

编写自己的密码通常是一个糟糕的主意,除非你有打破其他人的密码实现的经验。

这里没有一个例子validation密文 ,这使得它们容易受到位重写攻击。

如果你可以安装PECL扩展, libsodium甚至更好

 <?php // PECL libsodium 0.2.1 and newer /** * Encrypt a message * * @param string $message - message to encrypt * @param string $key - encryption key * @return string */ function safeEncrypt($message, $key) { $nonce = \Sodium\randombytes_buf( \Sodium\CRYPTO_SECRETBOX_NONCEBYTES ); return base64_encode( $nonce. \Sodium\crypto_secretbox( $message, $nonce, $key ) ); } /** * Decrypt a message * * @param string $encrypted - message encrypted with safeEncrypt() * @param string $key - encryption key * @return string */ function safeDecrypt($encrypted, $key) { $decoded = base64_decode($encrypted); $nonce = mb_substr($decoded, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); $ciphertext = mb_substr($decoded, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); return \Sodium\crypto_secretbox_open( $ciphertext, $nonce, $key ); } 

然后testing一下:

 <?php // This refers to the previous code block. require "safeCrypto.php"; // Do this once then store it somehow: $key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES); $message = 'We are all living in a yellow submarine'; $ciphertext = safeEncrypt($message, $key); $plaintext = safeDecrypt($ciphertext, $key); var_dump($ciphertext); var_dump($plaintext); 

这可以在任何情况下将数据传递给客户端(例如,没有服务器端存储的会话的encryptioncookie,encryption的URL参数等),并具有相当高的确定性,以致最终用户无法破译或可靠地篡改用它。

由于libsodium是跨平台的 ,这也使得与例如Java小程序或本地移动应用程序的PHP进行通信变得更容易。


注意:如果您特别需要在您的应用程序中添加由libsodium驱动的encryptioncookie,我的雇主Paragon Initiative Enterprises正在开发名为Halite的图书馆,为您完成所有这些工作。

有关信息MCRYPT_MODE_ECB不使用IV(初始化向量)。 ECB模式将消息分成块,每个块分别encryption。 我真的不推荐它

CBC模式使用IV来使每个消息都是唯一的。 build议使用CBC,而不是ECB。

例如:

 <?php $password = "myPassword_!"; $messageClear = "Secret message"; // 32 byte binary blob $aes256Key = hash("SHA256", $password, true); // for good entropy (for MCRYPT_RAND) srand((double) microtime() * 1000000); // generate random iv $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND); $crypted = fnEncrypt($messageClear, $aes256Key); $newClear = fnDecrypt($crypted, $aes256Key); echo "IV: <code>".$iv."</code><br/>". "Encrypred: <code>".$crypted."</code><br/>". "Decrypred: <code>".$newClear."</code><br/>"; function fnEncrypt($sValue, $sSecretKey) { global $iv; return rtrim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv)), "\0\3"); } function fnDecrypt($sValue, $sSecretKey) { global $iv; return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_CBC, $iv), "\0\3"); } 

你必须存储IV来解码每个消息(IV 不是秘密)。 每条消息都是独一无二的,因为每条消息都有独特的IV

  • 有关操作模式的更多信息(维基百科) 。

AESencryption很less有重要的注意事项:

  1. 切勿使用纯文本作为encryption密钥。 始终散列纯文本密钥,然后用于encryption。
  2. 始终使用随机IV(初始化vector)进行encryption和解密。 真正的随机化很重要。
  3. 如上所述,不要使用ecb模式,而应使用CBC

如果您正在使用MCRYPT_RIJNDAEL_128,请尝试使用rtrim rtrim($output, "\0\3") 。 如果string的长度小于16,解密函数将返回长度为16个字符的string,最后加上03。

你可以很容易地检查这个,例如通过尝试:

 $string = "TheString"; $decrypted_string = decrypt_function($stirng, $key); echo bin2hex($decrypted_string)."=".bin2hex("TheString"); 

您可以使用OpenSSL在PHP中进行简单且安全的AESencryption。 OpenSSL通常内置于PHP中,所以不需要外部依赖。 下面的例子:

  • 在CBC模式下使用AES256
  • 使用SHA256从提供的密码生成一个密钥
  • 生成encryption数据的hmac散列以进行完整性检查
  • 为每条消息生成一个随机IV
  • 将IV(16字节)和散列(32字节)加到密文上

IV是一个公共信息,需要随机为每个消息。 散列确保数据没有被篡改。

 function encrypt($plaintext, $password) { $method = "AES-256-CBC"; $key = hash('sha256', $password, true); $iv = openssl_random_pseudo_bytes(16); $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv); $hash = hash_hmac('sha256', $ciphertext, $key, true); return $iv . $hash . $ciphertext; } function decrypt($ivHashCiphertext, $password) { $method = "AES-256-CBC"; $iv = substr($ivHashCiphertext, 0, 16); $hash = substr($ivHashCiphertext, 16, 32); $ciphertext = substr($ivHashCiphertext, 48); $key = hash('sha256', $password, true); if (hash_hmac('sha256', $ciphertext, $key, true) !== $hash) return null; return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv); } 

用法:

 $encrypted = encrypt('Plaintext string.', 'password'); // this yields a binary string echo decrypt($encrypted, 'password'); // decrypt($encrypted, 'wrong password') === null 

这应该与其他AES实现兼容,但不是mcrypt ,因为mcrypt使用PKCS#5而不是PKCS#7。