PHP 中的 AES-256 加密

Posted

技术标签:

【中文标题】PHP 中的 AES-256 加密【英文标题】:AES-256 encryption in PHP 【发布时间】:2011-10-09 20:48:15 【问题描述】:

我需要一个 php 函数 AES256_encode($dataToEcrypt)$data 加密为 AES-256,而另一个 AES256_decode($encryptedData) 则相反。有谁知道这个函数应该有什么代码?

【问题讨论】:

这是一篇很好的博文,解释了如何使用 MCrypt 库:code-epicenter.com/how-to-use-mcrypt-library-in-php 【参考方案1】:

我需要一个 PHP 函数 AES256_encode($dataToEcrypt)$data 加密为 AES-256,而另一个 AES256_decode($encryptedData) 则相反。有谁知道这个函数应该有什么代码?

有一个difference between encrypting and encoding。

真的需要 AES-256 吗? AES-256 与 AES-128 的安全性并不重要。你更有可能在协议层搞砸而不是被黑客入侵,因为你使用的是 128 位分组密码而不是 256 位分组密码。

重要 - 使用库

defuse/php-encryption PECL libsodium Halite(libsodium 包装器,现已稳定)

快速而肮脏的 AES-256 实现

如果您有兴趣构建自己的不是为了在生产中部署它,而是为了您自己的教育,我提供了一个示例 AES256

/**
 * This is a quick and dirty proof of concept for ***.
 * 
 * @ref http://***.com/q/6770370/2224584
 * 
 * Do not use this in production.
 */
abstract class ExperimentalAES256DoNotActuallyUse

    /**
     * Encrypt with AES-256-CTR + HMAC-SHA-512
     * 
     * @param string $plaintext Your message
     * @param string $encryptionKey Key for encryption
     * @param string $macKey Key for calculating the MAC
     * @return string
     */
    public static function encrypt($plaintext, $encryptionKey, $macKey)
    
        $nonce = random_bytes(16);
        $ciphertext = openssl_encrypt(
            $plaintext,
            'aes-256-ctr',
            $encryptionKey,
            OPENSSL_RAW_DATA,
            $nonce
        );
        $mac = hash_hmac('sha512', $nonce.$ciphertext, $macKey, true);
        return base64_encode($mac.$nonce.$ciphertext);
    

    /**
     * Verify HMAC-SHA-512 then decrypt AES-256-CTR
     * 
     * @param string $message Encrypted message
     * @param string $encryptionKey Key for encryption
     * @param string $macKey Key for calculating the MAC
     */
    public static function decrypt($message, $encryptionKey, $macKey)
    
        $decoded = base64_decode($message);
        $mac = mb_substr($message, 0, 64, '8bit');
        $nonce = mb_substr($message, 64, 16, '8bit');
        $ciphertext = mb_substr($message, 80, null, '8bit');

        $calc = hash_hmac('sha512', $nonce.$ciphertext, $macKey, true);
        if (!hash_equals($calc, $mac)) 
            throw new Exception('Invalid MAC');
        
        return openssl_decrypt(
            $ciphertext,
            'aes-256-ctr',
            $encryptionKey,
            OPENSSL_RAW_DATA,
            $nonce
        );
    

用法

首先,生成两个密钥(是的,其中两个)并以某种方式存储它们。

$eKey = random_bytes(32);
$aKey = random_bytes(32);

然后加密/解密消息:

$plaintext = 'This is just a test message.';
$encrypted = ExperimentalAES256DoNotActuallyUse::encrypt($plaintext, $eKey, $aKey);
$decrypted = ExperimentalAES256DoNotActuallyUse::decrypt($encrypted, $eKey, $aKey);

如果您没有random_bytes(),请获取random_compat。

【讨论】:

你能解释一下为什么在生产中使用快速而肮脏的实现不是一个好主意吗? 你会发现你想要defuse/php-encryption,而不是自己滚动或从 *** 复制和粘贴。自己动手的唯一原因是创建玩具实现来自学。 是的,但是比如说我想处理从 CPP 通过套接字到 PHP 的流加密。我没有亲自研究过 defuse 的库,但是上面写的通用 AES256 是否有理由不适合?显然不是确切的代码,但就构建密码而言类似。 这个答案是否仍然是最新的? (只是检查)。我不知道岩盐的历史,但在我看来,目前有稳定版本的v3.2.0 可用 没有任何变化,除了 libsodium 将在 PHP 7.2 中登陆。【参考方案2】:
$key = '324325923495kdfgiert734t'; // key used for decryption in jasper code
$text = 'string_to_be_encrypted';
$encrypted = fnEncrypt($text, $key);




function fnEncrypt( $plaintext, $key )

$plaintext = pkcs5_pad($plaintext, 16);

return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, hex2bin($key), $plaintext, MCRYPT_MODE_ECB));




function pkcs5_pad ($text, $blocksize)

$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);




function hex2bin($hexdata) 

$bindata = "";

    for ($i = 0; $i < strlen($hexdata); $i += 2) 
    
      $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
    

return $bindata;

【讨论】:

请简要解释您的答案,使其对 OP 和其他读者更有用。 请添加解密功能。​​【参考方案3】:

看mcrypt module

取自here的AES-Rijndael示例

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
$key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
# show key size use either 16, 24 or 32 byte keys for AES-128, 192
# and 256 respectively
$key_size =  strlen($key);
echo "Key size: " . $key_size . "\n";
$text = "Meet me at 11 o'clock behind the monument.";
echo strlen($text) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
echo strlen($crypttext) . "\n";

这是decrypt function

【讨论】:

-1,AES-256 与RIJNDAEL-256不同AES 中的 256 指的是 key 大小,RIJNDAEL 中的 256 指的是块大小。 AES-256 与 256 位密钥一起使用时为 RIJNDAEL-128 @CodesInChaos 我已根据您的观察编辑了答案。现在答案应该是正确的。 我只想强调,如果可能进行主动攻击,MAC 就非常重要。众所周知的攻击是"padding oracle",其中收件人的反应会泄露有关明文的信息,从而允许通过查询收件人来逐字节恢复明文。 填充也需要注意:因为 php 中的 mcrypt 库只支持零长度填充。大多数人使用 pkcs#5 或 pkcs#7 填充。因此,如果在不同的平台/位置(例如:网络服务器与移动应用程序)进行加密和解密,请务必确保匹配填充 PHP 已弃用 mcrypt 库,它将在 7.1 之后从 PHP 版本中删除。因此,使用建议 mcrypt 会弃用此答案。见php.net/manual/en/migration71.deprecated.php【参考方案4】:

MCRYPT_RIJNDAEL_256 不等同于 AES_256。

从 AES 中解密 RIJNDAEL 的方法是使用 MCRYPT_RIJNDAEL_128 并在加密前填充要加密的字符串

AES-256 的 BlockSize=128bit 和 KeySize=256bit Rijndael-256 的 BlockSize=256bit 和 KeySize=256bit

只是 AES/Rijndael 128 位是相同的。 Rijndael-192 和 Rijndael-256 与 AES-192 和 AES-256 不同(块大小和轮数不同)。

【讨论】:

你当然是对的,但这篇文章并没有真正回答这个问题。 感谢@CodesInChaos。使用 openssl 从 AES 解密 RIJNDAEL 的方法是使用 MCRYPT_RIJNDAEL_128 并在使用以下函数加密之前填充要加密的字符串:

以上是关于PHP 中的 AES-256 加密的主要内容,如果未能解决你的问题,请参考以下文章

iOS 中的 AES256 NSString 加密

C++ 和 Qt 5 中的 AES 256 加密

有没有办法过滤 aes 256 gcm 加密数据库中的数据?

QT:AES-256-CBC 根据 PHP 代码在 C++ 中加密/解密

PHP AES 256 加密奇怪的字符

python Crypto AES-256-ECB 与PHP之间完成加解密