PHP AES 加密/解密请使用现有的安全 PHP 加密库 如果您可以安装PECL扩展,libsodium会更好

我找到了一个在PHP中en/解码字符串的示例。起初它看起来非常好,但它不会工作:-(

有谁知道问题是什么?

$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)));
}

结果是:

加密:boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

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


答案 1

请使用现有的安全 PHP 加密库

编写自己的加密通常是一个坏主意,除非您有破坏其他人的加密实现的经验。

此处的示例均未对密文进行身份验证,这使得它们容易受到位重写攻击。

如果您可以安装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
    );
}    

然后进行测试:

<?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);

这可以用于您将数据传递到客户端的任何情况(例如,没有服务器端存储的会话的加密cookie,加密的URL参数等),并且具有相当高的确定性,即最终用户无法破译或可靠地篡改它。

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


注意:如果您特别需要将libsodium驱动的加密cookie添加到您的应用程序中,我的雇主Paragon Initiative Enterprises正在开发一个名为Halete的库,为您完成所有这些工作。


答案 2

如果您不想对15行代码中可解决的问题使用重依赖项,请使用内置的OpenSSL函数。大多数PHP安装都带有OpenSSL,它在PHP中提供快速,兼容和安全的AES加密。好吧,只要您遵循最佳实践,它就是安全的。

下面的代码:

  • 在 CBC 模式下使用 AES256
  • 与其他 AES 实现兼容,但与 mcrypt 不兼容,因为 mcrypt 使用 PKCS#5 而不是 PKCS#7。
  • 使用 SHA256 从提供的密码生成密钥
  • 生成加密数据的 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 . $iv, $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_equals(hash_hmac('sha256', $ciphertext . $iv, $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

编辑:更新为使用并将IV添加到哈希。hash_equals


推荐