PHP AES加密/解密

And*_*ang 46 php encryption cryptography aes encryption-symmetric

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

有谁知道问题是什么?

$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)));
}
Run Code Online (Sandbox Code Playgroud)

结果是:

加密: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

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

Sco*_*ski 78

请使用现有的安全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
    );
}    
Run Code Online (Sandbox Code Playgroud)

然后测试一下:

<?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);
Run Code Online (Sandbox Code Playgroud)

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

由于libsodium是跨平台的,因此也可以更轻松地与PHP通信,例如Java applet或本机移动应用程序.


注意:如果您特别需要将libsodium提供的加密cookie添加到您的应用程序,我的雇主Paragon Initiative Enterprises正在开发一个名为Halite的库,它可以为您完成所有这些工作.


zz1*_*433 53

$sDecrypted并且$sEncrypted 在您的代码中未定义.查看有效的解决方案(但不安全!):


停!

这个例子不安全!不要用它!


$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"
    );
}
Run Code Online (Sandbox Code Playgroud)

但是这个代码中存在其他问题使得它不安全,特别是使用ECB(不是加密模式,只能在其上定义加密模式的构建块).请参阅Fab Sa的答案,快速解决最严重的问题,以及Scott如何做到这一点的答案.

  • 我正在使用此代码,我发现了一个错误.不应该使用装饰!它应该是带有第二个参数"\ 0"的rtrim().在极少数情况下,加密值的第一个或最后一个字符可能是空格或返回,解密出错...... (16认同)
  • -1:在`encrypt`和`decrypt`函数中传递不同的IV根本没有意义.这种方法的唯一原因是因为ECB模式根本不使用初始化向量,因此任何值都会产生并产生相同的输出. (8认同)
  • -1表示ECB.请参阅[维基百科条目](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29),了解有关块密码模式选择不佳的详细信息. (5认同)
  • 我不知道为什么这个答案如此投票.仅仅因为它"有效"并不意味着它是安全的,应该在生产环境中使用.MCRYPT_MODE_ECB的使用非常严重,甚至PHP mcrypt_ecb函数自PHP 5.5.0起已被废弃.非常不鼓励依赖此功能.相反,你应该使用MCRYPT_MODE_CBC模式:http://wpy.me/blog/15-encrypt-and-decrypt-data-in-php-using-aes-256 (4认同)
  • 你能再混淆一下了吗?我担心,只要付出一些努力,我仍然可以阅读你的代码所做的事情.</ sarcasm mode>代码应该是自我描述的.给mcrypt_get_iv_size的输出提供一个合适的变量名,然后使用它.这种缩进很难读懂.除非你已经习惯了Lisp,但是大多数PHP程序员都不是我会说的.我不是说OP的(问题提问者)代码更好,但作为一个好的答案,你也可以改进代码. (3认同)

bla*_*ade 39

如果您不想对 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);
}
Run Code Online (Sandbox Code Playgroud)

用法:

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

echo decrypt($encrypted, 'password');
// decrypt($encrypted, 'wrong password') === null
Run Code Online (Sandbox Code Playgroud)

  • 使用相等运算符比较散列不是一个好习惯,您应该使用`hash_equals()`,因为它容易受到定时攻击,更多信息[here](https://wiki.php.net/rfc/timing_attack ) (2认同)
  • 这个答案已经差不多了,但是需要...... 1)更好的KDF,SHA-256是一个非常差的KDF。至少使用 PBKDF2,但 Argon2/bcrypt 会更好。2) IV 需要包含在 HMAC 中 - HMAC 的目的是确保解密要么得到明文,要么失败 - 不包含 IV 会导致用户“认为”他们得到了原始明文,但不是。3)在比较哈希值时使用时间安全的比较,否则此代码“可能”容易受到定时攻击。4) 不要对 HMAC 使用与 AES 相同的密钥。 (2认同)
  • @MikkoRantalainen 我们不能假设密钥是安全的,特别是因为它不是密钥,而是人类选择的低熵密码。如果我们使用 SHA-256 来导出加密密钥(这需要的时间可以忽略不计),那么对密码的暴力攻击就非常容易了。然而,如果我们使用 PBKDF2 或 Argon2,我们可以微调派生密码所需的时间(想想几百毫秒),那么暴力破解就变得不太可行。这与我们不使用 SHA-256 作为密码哈希的原因几乎完全相同。 (2认同)
  • 如果要将其存储在数据库中,您应该对加密函数的返回值执行base64_encode,并对解密函数的$ivHashCiphertext 值执行base64_decode。如果不是,您可能会遇到数据库编码问题。 (2认同)
  • @naman1994 输出是二进制 blob,因此您需要将其编码为纯文本 - 即。使用base64_encode、bin2hex等 (2认同)

Fab*_* Sa 26

有关信息MCRYPT_MODE_ECB不使用IV(初始化向量).ECB模式将您的消息划分为块,每个块都单独加密.我真的不推荐它.

CBC模式使用IV使每条消息都是唯一的.建议使用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");
}
Run Code Online (Sandbox Code Playgroud)

您必须存储IV以解码每条消息(IV 不是秘密的).每条消息都是唯一的,因为每条消息都有唯一的IV.

  • 请注意,上面的代码不使用AES,也不使用PKCS#7填充,这意味着它将与其他任何系统不兼容.我是修复`mcrypt_encrypt`的示例代码的人. (4认同)
  • +1.最佳答案生成随机IV以馈送不需要任何系统(ECB)的系统. (2认同)

Dun*_*kun 6

这是一个AES encryption使用 实现的可行解决方案openssl。它使用密码块链接模式(CBC 模式)。因此,除了 dataand 之外key,您还可以指定ivandblock size

 <?php
      class AESEncryption {

            protected $key;
            protected $data;
            protected $method;
            protected $iv;

            /**
             * Available OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
             *
             * @var type $options
             */
            protected $options = 0;

            /**
             * 
             * @param type $data
             * @param type $key
             * @param type $iv
             * @param type $blockSize
             * @param type $mode
             */
            public function __construct($data = null, $key = null, $iv = null, $blockSize = null, $mode = 'CBC') {
                $this->setData($data);
                $this->setKey($key);
                $this->setInitializationVector($iv);
                $this->setMethod($blockSize, $mode);
            }

            /**
             * 
             * @param type $data
             */
            public function setData($data) {
                $this->data = $data;
            }

            /**
             * 
             * @param type $key
             */
            public function setKey($key) {
                $this->key = $key;
            }

            /**
             * CBC 128 192 256 
              CBC-HMAC-SHA1 128 256
              CBC-HMAC-SHA256 128 256
              CFB 128 192 256
              CFB1 128 192 256
              CFB8 128 192 256
              CTR 128 192 256
              ECB 128 192 256
              OFB 128 192 256
              XTS 128 256
             * @param type $blockSize
             * @param type $mode
             */
            public function setMethod($blockSize, $mode = 'CBC') {
                if($blockSize==192 && in_array('', array('CBC-HMAC-SHA1','CBC-HMAC-SHA256','XTS'))){
                    $this->method=null;
                    throw new Exception('Invalid block size and mode combination!');
                }
                $this->method = 'AES-' . $blockSize . '-' . $mode;
            }

            /**
             * 
             * @param type $data
             */
            public function setInitializationVector($iv) {
                $this->iv = $iv;
            }

            /**
             * 
             * @return boolean
             */
            public function validateParams() {
                if ($this->data != null &&
                        $this->method != null ) {
                    return true;
                } else {
                    return FALSE;
                }
            }

            //it must be the same when you encrypt and decrypt
            protected function getIV() { 
                return $this->iv;
            }

             /**
             * @return type
             * @throws Exception
             */
            public function encrypt() {
                if ($this->validateParams()) { 
                    return trim(openssl_encrypt($this->data, $this->method, $this->key, $this->options,$this->getIV()));
                } else {
                    throw new Exception('Invalid params!');
                }
            }

            /**
             * 
             * @return type
             * @throws Exception
             */
            public function decrypt() {
                if ($this->validateParams()) {
                   $ret=openssl_decrypt($this->data, $this->method, $this->key, $this->options,$this->getIV());

                   return   trim($ret); 
                } else {
                    throw new Exception('Invalid params!');
                }
            }

        }
Run Code Online (Sandbox Code Playgroud)

使用示例:

<?php
        $data = json_encode(['first_name'=>'Dunsin','last_name'=>'Olubobokun','country'=>'Nigeria']);
        $inputKey = "W92ZB837943A711B98D35E799DFE3Z18";
        $iv = "tuqZQhKP48e8Piuc";
        $blockSize = 256;
        $aes = new AESEncryption($data, $inputKey, $iv, $blockSize);
        $enc = $aes->encrypt();
        $aes->setData($enc);
        $dec=$aes->decrypt();
        echo "After encryption: ".$enc."<br/>";
        echo "After decryption: ".$dec."<br/>";
Run Code Online (Sandbox Code Playgroud)


Mar*_*cas 6

这些是使用AES256 CBC通过 PHP 加密/解密字符串的紧凑方法:

function encryptString($plaintext, $password, $encoding = null) {
    $iv = openssl_random_pseudo_bytes(16);
    $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
    return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}

function decryptString($ciphertext, $password, $encoding = null) {
    $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
    if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
    return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}
Run Code Online (Sandbox Code Playgroud)

用法:

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");
Run Code Online (Sandbox Code Playgroud)

编辑:这是新版本的函数,使用AES256 GCMPBKDF2作为密钥派生,更安全。

function str_encryptaesgcm($plaintext, $password, $encoding = null) {
    if ($plaintext != null && $password != null) {
        $keysalt = openssl_random_pseudo_bytes(16);
        $key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
        $tag = "";
        $encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
        return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
    }
}

function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
    if ($encryptedstring != null && $password != null) {
        $encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
        $keysalt = substr($encryptedstring, 0, 16);
        $key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
        $ivlength = openssl_cipher_iv_length("aes-256-gcm");
        $iv = substr($encryptedstring, 16, $ivlength);
        $tag = substr($encryptedstring, -16);
        return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

$enc = str_encryptaesgcm("mysecretText", "myPassword", "base64"); // return a base64 encrypted string, you can also choose hex or null as encoding.
$dec = str_decryptaesgcm($enc, "myPassword", "base64");
Run Code Online (Sandbox Code Playgroud)