代码之家  ›  专栏  ›  技术社区  ›  Andreas Prang

PHP AES加密/解密

  •  41
  • Andreas Prang  · 技术社区  · 16 年前

    有人知道问题出在哪里吗?

    $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‰Â—

    8 回复  |  直到 10 年前
        1
  •  59
  •   Clonkex    9 年前

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

    加密 Fab Sa's answer 快速解决最糟糕的问题 Scott's answer 如何做到这一点。

        2
  •  84
  •   Scott Arciszewski    10 年前

    secure PHP encryption library

    一般来说,编写自己的密码是个坏主意,除非你有破坏别人密码实现的经验。

    这里没有一个例子 authenticate the ciphertext

    如果你能安装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 is cross-platform ,这也使得从Java小程序或本地移动应用程序与PHP进行通信变得更容易。


    Paragon Initiative Enterprises 正在开发一个叫做 Halite

        3
  •  56
  •   blade    7 年前

    不想使用一个沉重的依赖 OpenSSL 功能。大多数PHP安装都附带OpenSSL,它在PHP中提供了快速、兼容和安全的AES加密。好吧,只要你遵循最佳实践,它是安全的。

    以下代码:

    • 在CBC模式下使用AES256
    • 不是mcrypt ,因为mcrypt使用PKCS#5而不是PKCS#7。
    • 使用SHA256从提供的密码生成密钥
    • 为完整性检查生成加密数据的hmac哈希
    • 将IV(16字节)和哈希(32字节)前置到密文
    • 应该很安全

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

    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
    

    编辑:更新为使用 hash_equals 在散列中加了IV。

        4
  •  27
  •   Fabien Sa Shami    11 年前

    供参考 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");
    }
    

    你必须库存的IV解码每个信息(IV是 不 秘密)。每条消息都是唯一的,因为每条消息都有一个唯一的IV。

        5
  •  3
  •   Naveen DA    9 年前

    1. 切勿将纯文本用作加密密钥。总是散列纯文本密钥,然后用于加密。
    2. 真正随机化
    3. 如上所述,不要使用 模式,使用 CBC
        6
  •  3
  •   Dunsin Olubobokun    7 年前

    AES encryption -使用实现 openssl data 和 key ,您可以指定 iv 和 block 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!');
                    }
                }
    
            }
    

    示例用法:

    <?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/>";
    
        7
  •  2
  •   Marco Concas    6 年前

    AES256 CBC公司 :

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

    $enc = encryptString("mysecretText", "myPassword");
    $dec = decryptString($enc, "myPassword");
    
        8
  •  1
  •   ЯegDwight kri    14 年前

    如果您使用的是MCRYPT\u RIJNDAEL\u 128,请尝试 rtrim($output, "\0\3")

    您可以很容易地检查这一点,例如通过尝试:

    $string = "TheString";
    $decrypted_string = decrypt_function($stirng, $key);
    
    echo bin2hex($decrypted_string)."=".bin2hex("TheString");
    
        9
  •  1
  •   Mikko Rantalainen    5 年前

    这是一个改进的版本 based on code written by blade

    • 添加评论
    • 在抛出之前覆盖参数,以避免泄漏机密
    • 检查openssl和hmac函数的返回值

    class Crypto
    {
        /**
         * Encrypt data using OpenSSL (AES-256-CBC)
         * @param string $plaindata Data to be encrypted
         * @param string $cryptokey key for encryption (with 256 bit of entropy)
         * @param string $hashkey key for hashing (with 256 bit of entropy)
         * @return string IV+Hash+Encrypted as raw binary string. The first 16
         *     bytes is IV, next 32 bytes is HMAC-SHA256 and the rest is
         *     $plaindata as encrypted.
         * @throws Exception on internal error
         *
         * Based on code from: https://stackoverflow.com/a/46872528
         */
        public static function encrypt($plaindata, $cryptokey, $hashkey)
        {
            $method = "AES-256-CBC";
            $key = hash('sha256', $cryptokey, true);
            $iv = openssl_random_pseudo_bytes(16);
    
            $cipherdata = openssl_encrypt($plaindata, $method, $key, OPENSSL_RAW_DATA, $iv);
    
            if ($cipherdata === false)
            {
                $cryptokey = "**REMOVED**";
                $hashkey = "**REMOVED**";
                throw new \Exception("Internal error: openssl_encrypt() failed:".openssl_error_string());
            }
    
            $hash = hash_hmac('sha256', $cipherdata.$iv, $hashkey, true);
    
            if ($hash === false)
            {
                $cryptokey = "**REMOVED**";
                $hashkey = "**REMOVED**";
                throw new \Exception("Internal error: hash_hmac() failed");
            }
    
            return $iv.$hash.$cipherdata;
        }
    
        /**
        * Decrypt data using OpenSSL (AES-256-CBC)
         * @param string $encrypteddata IV+Hash+Encrypted as raw binary string
         *     where the first 16 bytes is IV, next 32 bytes is HMAC-SHA256 and
         *     the rest is encrypted payload.
         * @param string $cryptokey key for decryption (with 256 bit of entropy)
         * @param string $hashkey key for hashing (with 256 bit of entropy)
         * @return string Decrypted data
         * @throws Exception on internal error
         *
         * Based on code from: https://stackoverflow.com/a/46872528
         */
        public static function decrypt($encrypteddata, $cryptokey, $hashkey)
        {
            $method = "AES-256-CBC";
            $key = hash('sha256', $cryptokey, true);
            $iv = substr($encrypteddata, 0, 16);
            $hash = substr($encrypteddata, 16, 32);
            $cipherdata = substr($encrypteddata, 48);
    
            if (!hash_equals(hash_hmac('sha256', $cipherdata.$iv, $hashkey, true), $hash))
            {
                $cryptokey = "**REMOVED**";
                $hashkey = "**REMOVED**";
                throw new \Exception("Internal error: Hash verification failed");
            }
    
            $plaindata = openssl_decrypt($cipherdata, $method, $key, OPENSSL_RAW_DATA, $iv);
    
            if ($plaindata === false)
            {
                $cryptokey = "**REMOVED**";
                $hashkey = "**REMOVED**";
                throw new \Exception("Internal error: openssl_decrypt() failed:".openssl_error_string());
            }
    
            return $plaindata;
        }
    }
    

    如果您确实无法拥有正确的加密和哈希密钥,但必须使用用户输入的密码作为唯一机密,则可以执行以下操作:

    /**
     * @param string $password user entered password as the only source of
     *   entropy to generate encryption key and hash key.
     * @return array($encryption_key, $hash_key) - note that PBKDF2 algorithm
     *   has been configured to take around 1-2 seconds per conversion
     *   from password to keys on a normal CPU to prevent brute force attacks.
     */
    public static function generate_encryptionkey_hashkey_from_password($password)
    {
        $hash = hash_pbkdf2("sha512", "$password", "salt$password", 1500000);
        return str_split($hash, 64);
    }
    
        10
  •  0
  •   M_R_K    8 年前

    请在此处查找更多信息- http://php.net/manual/en/intro.sodium.php