PHP中的AES-256加密

28

我需要一个PHP函数,AES256_encode($dataToEcrypt)$data 加密成AES-256,并且另一个函数 AES256_decode($encryptedData) 则相反地解密。有人知道这些函数应该包含什么代码吗?


这是一篇很好的博客文章,解释了如何使用MCrypt库:http://code-epicenter.com/how-to-use-mcrypt-library-in-php/ - MrD
5个回答

20

请查看mcrypt模块

以下是从此处获取的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";

这是解密函数


14
-1,AES-256 与 RIJNDAEL-256不同的。AES 中的 256 指的是密钥大小,而 RIJNDAEL 中的 256 指块大小。当使用 256 位密钥时,AES-256 等同于 RIJNDAEL-128 - ircmaxell
2
@CodesInChaos 根据您的观察,我已经编辑了答案。现在答案应该是正确的。 - Fabio
2
我想强调的是,如果可能存在主动攻击,MACs 真的非常重要。众所周知的一种攻击是“填充预言”攻击,在这种攻击中,接收者的反应会泄露有关明文的信息,从而通过查询接收者逐字节恢复明文。 - CodesInChaos
1
填充也很重要:因为php中的mcrypt库仅支持零长度填充。而大多数人使用pkcs#5或pkcs#7填充。因此,如果在不同的平台/位置(例如:Web服务器与移动应用程序)上进行加密和解密,则始终确保匹配填充。 - DEzra
2
PHP已经弃用了mcrypt库,它将在PHP 7.1版本之后被移除。因此,建议不要使用mcrypt,否则这个答案也会被弃用。请参阅http://php.net/manual/en/migration71.deprecated.php。 - Dennis
显示剩余3条评论

18

我需要一个PHP函数,AES256_encode($dataToEcrypt) 用于将 $data 加密成AES-256,另外一个函数 AES256_decode($encryptedData) 则实现相反的功能。是否有人知道这些函数应该是什么代码?

加密和编码之间有区别

你真的需要AES-256吗?与AES-128相比,AES-256的安全性并不显著;您更可能在协议层面出错,而不是因为使用了128位块密码而被黑客攻击了。

重要提示 - 使用库

PHP用户的流程图

快速而不精确的AES-256实现

如果您有兴趣构建自己的实现但不是为了在生产中部署,而是仅仅为了自己的教育,我包含了一个样本AES256

/**
 * This is a quick and dirty proof of concept for StackOverflow.
 * 
 * @ref https://dev59.com/J2w15IYBdhLWcg3wMY6L
 * 
 * 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


4
如果快而简单的实现能够满足需求,为什么不在生产中使用它呢?你能解释一下为什么这不是一个好主意吗?(注意:翻译保持原意,尽可能通俗易懂,不添加解释和额外内容)。 - Nathan F.
你会发现使用 defuse/php-encryption 而不是自己编写或从 StackOverflow 复制粘贴,这样更好。自己编写的唯一原因就是创建玩具实现来教自己。 - Scott Arciszewski
2
是的,但是举个例子,如果我想要处理从CPP到PHP的套接字流加密之类的东西,我个人还没有研究过defuse的库,但是是否有理由认为上面写的通用AES256不适用?显然不是完全相同的代码,但是构建密码器方面可以使用类似的东西。 - Nathan F.
这个答案还是最新的吗?(只是确认一下)。我不知道Halite的历史,但目前似乎有一个稳定版本v3.2.0可用。 - Dennis
除了libsodium即将在PHP 7.2中出现,没有任何变化。 - Scott Arciszewski
显示剩余2条评论

15

MCRYPT_RIJNDAEL_256不等同于AES_256。

将RIJNDAEL解密为AES的方法是使用MCRYPT_RIJNDAEL_128并在加密之前填充要加密的字符串。

AES-256的BlockSize为128位,KeySize为256位。 Rijndael-256的BlockSize为256位,KeySize为256位。

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


3
你说得没错,但是这篇文章并没有真正回答问题。 - CodesInChaos
感谢@CodesInChaos。使用MCRYPT_RIJNDAEL_128并在加密之前填充要加密的字符串是将RIJNDAEL从AES解密的方法,具体操作如下:<?php function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } ?> - Behzad-Ravanbakhsh

-1
您可以使用PHP中的password_hash函数对数据进行加密和解密。您还需要一个成本函数。请确保您的PHP环境启用了OpenSSL和Mcrypt扩展。
<?php
$plaintext = 'My secret message 1234';
$password = '3sc3RLrpd17';
$method = 'aes-256-cbc';

$key = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

// IV must be exact 16 chars (128 bit)
$iv = random_bytes(16);

$encrypted = base64_encode(openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv));
$decrypted = openssl_decrypt(base64_decode($encrypted), $method, $key, OPENSSL_RAW_DATA, $iv);

echo 'plaintext=' . $plaintext . "\n";
echo 'cipher=' . $method . "\n";
echo 'encrypted to: ' . $encrypted . "\n";
echo 'decrypted to: ' . $decrypted . "\n\n";
?>
  • $plaintext: 这个变量保存我们想要加密的消息。在这个例子中,“My secret message 1234”是明文。
  • $password: 在这里,我们定义用于生成加密密钥的密码。
  • $method: 我们将加密方法设置为“aes-256-CBC”,这是使用Cipher Block Chaining (CBC)模式的AES-256加密模式。
  • $key: 该代码使用提供的密码使用password_hash()函数生成加密密钥,使用PASSWORD_BCRYPT算法和成本因素12。
  • $iv: 初始化向量(IV)对于AES-256在CBC模式下是必需的,并且必须正好16字节长(128位)。
  • $encrypted: 使用openssl_encrypt()函数获取加密后的密文,该函数接受明文、加密方法、加密密钥、IV和其他参数。然后对密文进行Base64编码以便表示。
  • $decrypted: 为了确保解密过程正常工作,我们使用openssl_decrypt()函数来反转加密过程。这将在解码Base64表示后返回原始明文。

来源:PHP中的安全AES-256加密和解密


1
安全警告:请不要使用静态初始化向量,因为这会使整个加密过程变得容易受到攻击。最好使用随机的初始化向量,并将其附加在加密密码之前。 - Michael Fehr
@MichaelFehr 谢谢,这是为了代码的简洁性。我已根据您的建议修改了答案。 - Mostafa Safarian
看起来好多了,但是作为一个建议,打印出随机IV(同样使用Base64编码),这样以后就可以进行解密了(你的代码会正常工作,因为解密紧跟着加密,但是当只存储密文和口令时,无法正确获取解密后的[前16个字节]明文),谢谢。 - Michael Fehr
你的回答可以通过提供更多支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便其他人可以确认你的答案是否正确。你可以在帮助中心找到关于如何写好回答的更多信息。 - Community

-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和其他读者更有用。 - Mohit Jain
1
请添加解密函数。 - user216084

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接