[php] PHP AES encrypt / decrypt

Here's an improved version based on code written by blade

  • add comments
  • overwrite arguments before throwing to avoid leaking secrets with the exception
  • check return values from openssl and hmac functions

The code:

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

If you truly cannot have proper encryption and hash keys but have to use an user entered password as the only secret, you can do something like this:

/**
 * @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);
}

Examples related to php

I am receiving warning in Facebook Application using PHP SDK Pass PDO prepared statement to variables Parse error: syntax error, unexpected [ Preg_match backtrack error Removing "http://" from a string How do I hide the PHP explode delimiter from submitted form results? Problems with installation of Google App Engine SDK for php in OS X Laravel 4 with Sentry 2 add user to a group on Registration php & mysql query not echoing in html with tags? How do I show a message in the foreach loop?

Examples related to encryption

mcrypt is deprecated, what is the alternative? Remove 'b' character do in front of a string literal in Python 3 How to resolve the "EVP_DecryptFInal_ex: bad decrypt" during file decryption How to decrypt Hash Password in Laravel RSA encryption and decryption in Python How to fix Invalid AES key length? gpg decryption fails with no secret key error 7-Zip command to create and extract a password-protected ZIP file on Windows? How do I encrypt and decrypt a string in python? AES Encrypt and Decrypt

Examples related to cryptography

Failed to install Python Cryptography package with PIP and setup.py C# RSA encryption/decryption with transmission How do you Encrypt and Decrypt a PHP String? Example of AES using Crypto++ How to encrypt/decrypt data in php? How to decrypt a SHA-256 encrypted string? Simplest two-way encryption using PHP Padding is invalid and cannot be removed? Given final block not properly padded Getting RSA private key from PEM BASE64 Encoded private key file

Examples related to aes

How to fix Invalid AES key length? AES Encrypt and Decrypt How to create a secure random AES key in Java? Initial bytes incorrect after Java AES/CBC decryption Example of AES using Crypto++ Java AES encryption and decryption How to do encryption using AES in Openssl C# Example of AES256 encryption using System.Security.Cryptography.Aes Image encryption/decryption using AES256 symmetric block ciphers Comparison of DES, Triple DES, AES, blowfish encryption for data

Examples related to encryption-symmetric

How do you Encrypt and Decrypt a PHP String? How to encrypt/decrypt data in php? Simplest two-way encryption using PHP PHP AES encrypt / decrypt