AES-256 encryption in PHP
Asked Answered
C

4

28

I need a PHP function, AES256_encode($dataToEcrypt) to encrypt the $data into AES-256 and another one AES256_decode($encryptedData) do the opposite. Does anyone know what code should this functions have?

Centromere answered 21/7, 2011 at 1:24 Comment(1)
Here is a good blog post explaining how to work with MCrypt library: code-epicenter.com/how-to-use-mcrypt-library-in-phpDesman
C
21

Look at the mcrypt module

AES-Rijndael example taken from here

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

This is the decrypt function

Clothesline answered 21/7, 2011 at 1:27 Comment(8)
Yes, there are even examples on this page of how to encrypt/decrypt AES: php.net/manual/en/ref.mcrypt.phpColettacolette
Thanks, this was what I was looking for. I thought RIJNDAEL and AES were two different algorithms.Centromere
-1, AES-256 is different from RIJNDAEL-256. The 256 in AES refers to the key size, where the 256 in RIJNDAEL refers to block size. AES-256 is RIJNDAEL-128 when used with a 256 bit key.Higgledypiggledy
-1 it's bad in several ways: 1) It doesn't use AES (see ircmaxell's comment) 2) It uses ECB 3) It doesn't include a MAC and thus is vulnerable to active attacks. 4) It uses a password as key. Either use a random key, or use properly hashed password. 5) It's better to use /dev/urandrom to generate the iv (simply pass MCRYPT_DEV_URANDOM to create_iv)Wriggly
@Wriggly I've edited the answer according to your observations. Now the answer should be correct.Clothesline
I just want to emphasize that MACs are really important if active attacks are possible. A well known attack is the "padding oracle" where the reaction of the recipient leaks information about the plaintext allowing byte-by-byte recovery of the plaintext by querying the recipient.Wriggly
Padding is also important to note: as the mcrypt library in php only supports zero length padding. Where as most people using pkcs#5 or pkcs#7 padding. So always make sure you match the padding up if encrypting and decrypting in different platforms/places (for example: webserver vs mobile app)Duvalier
PHP has deprecated mcrypt library, it will be removed from PHP version after 7.1. As such, using suggesting mcrypt makes this answer deprecated. See php.net/manual/en/migration71.deprecated.phpFilomenafiloplume
C
18

I need a PHP function, AES256_encode($dataToEcrypt) to encrypt the $data into AES-256 and another one AES256_decode($encryptedData) do the opposite. Does anyone know what code should this functions have?

There is a difference between encrypting and encoding.

Do you really need AES-256? The security of AES-256 versus AES-128 isn't that significant; you're more likely to screw up at the protocol layer than get hacked because you used a 128-bit block cipher instead of a 256-bit block cipher.

Important - Use A Library

A flowchart for PHP users

A Quick and Dirty AES-256 Implementation

If you're interested in building your own not for the sake of deploying it in production but rather for the sake of your own education, I've included a sample AES256

/**
 * This is a quick and dirty proof of concept for StackOverflow.
 * 
 * @ref https://mcmap.net/q/490021/-aes-256-encryption-in-php/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
        );
    }
}

Usage

First, generate two keys (yes, two of them) and store them somehow.

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

Then to encrypt/decrypt messages:

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

If you don't have random_bytes(), get random_compat.

Catherinacatherine answered 13/10, 2015 at 20:41 Comment(7)
Can you explain why it would not be a good idea to use the quick-and-dirty implementation in production if it does what is needed?Casanova
You'll find that you want defuse/php-encryption rather than rolling your own or copying and pasting from StackOverflow. The only reason to roll your own is to create toy implementations to teach yourself.Catherinacatherine
Yes, but say for example I wanted to handle something like stream ciphering from CPP over a socket to PHP. I've not personally looked into defuse's library, but is there a reason the generic AES256 written above wouldn't be suitable? Clearly not the exact code, but something similar as far as building the cipher.Casanova
is this answer still up to date? (Just checking). I do not know the history of halite, but it seems to me that there is currently stable version of v3.2.0 that's availableFilomenafiloplume
Nothing has changed, except libsodium is due to land in PHP 7.2.Catherinacatherine
What is "$ciphertext" in "decrypt" methodPrepotency
@ScottArciszewski, The other reason would be "not renting whole building just to store a piece of paper".Satinwood
C
17

MCRYPT_RIJNDAEL_256 is not equivalent to AES_256.

The way to make RIJNDAEL be decrypted from AES is to use MCRYPT_RIJNDAEL_128 and padd the string to encrypt before encrypting

AES-256 has BlockSize=128bit and KeySize=256bit Rijndael-256 has BlockSize=256bit and KeySize=256bit

Just AES/Rijndael 128bit are identical. Rijndael-192 and Rijndael-256 are not identical to AES-192 and AES-256 (block sizes and number of rounds differ).

Crustacean answered 22/6, 2013 at 10:41 Comment(2)
You're certainly correct, but this post doesn't really answer the question.Wriggly
Thanks @CodesInChaos. The way to make RIJNDAEL be decrypted from AES with openssl is to use MCRYPT_RIJNDAEL_128 and padd the string to encrypt before encrypting with the follwing function: <?php function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } ?>Crustacean
P
-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;
}
Petrify answered 27/5, 2014 at 8:7 Comment(2)
Please explain your answer in brief to make it more useful for OP and other readers.Hett
Kindly add the decrypt function also.Contredanse

© 2022 - 2024 — McMap. All rights reserved.