Posted by & filed under Crypto, PHP.

PHP has a lot of available documentation. So much that Googling just about any PHP function provides a php.net result on the first page and a good majority of the content is accurate. And when it’s not, the public comments usually fill in the holes. The OpenSSL cryptography extension is one part of php.net that is very lacking, so much that you’ll even be greeted with Warning: this function is currently not documented; only its argument list is available for both openssl_encrypt and openssl_decrypt — perhaps the two most commonly looked up OpenSSL functions.

Environr
The future of environment configuration is here.

Here’s the full example. We’ll jump into the details below.

Update — 4/3/2017 — Thanks to those commenters who pointed out the issue with the initialization vector clashing. The gist has been updated to base64 encode $iv to mitigate.

This example regenerates the encryption key each time it runs. This is most likely not what you want. Ideally, the encryption key or password should be kept somewhere safe and only readable by the process that’s responsible for encrypting/decrypting.

After defineing our cipher, we generate the encryption key / password using openssl_random_pseudo_bytes. Here we’re creating a 32-byte or 256-bit key (the largest supported by AES).

Next, we create an initialization vector required by the openssl_encrypt function (well, technically it’s not required but you should use it). We use the same openssl_random_pseudo_bytes function used to generate the key, but this time we provide it with openssl_cipher_iv_length to generate the appropriately sized initialization vector for our cipher.

After creating some data to encrypt we use openssl_encrypt to create our ciphertext. If an initialization vector was used, we must be able to access it again for decryption so the simplest way to do this is to append it to our ciphertext with a separator. Because the initialization vector is not confidential, there’s no need to further encrypt it.

The decryption process starts by splitting the ciphertext into the original ciphertext and the initialization vector. We can then call openssl_decrypt, providing the original ciphertext, cipher, encryption key, and the initialization vector. The resulting value should match our unencrypted with which we started.

 

Sign up for Turret.IO — the only data-driven marketing platform made specifically for developers.

21 Responses to “The Missing PHP AES Encryption Example”

  1. Anonymous

    Great work. It really helped me a lot! Previously I was using an algorithm called rijndael but openssl one seems much neater. Kudos to you! 😀

    Reply
    • Fred

      Actually Rijndael is the cipher algorithm used by AES. And OpenSSL is not an algorithm, it’s a library.

      Reply
  2. P. Dantic

    Thanks for this. Your example could be made slightly clearer by using:

    list($encrypted, $iv) = explode(‘:’, $encrypted);

    Reply
  3. Jos

    Great article! Probably good to tell that if you want to store the encrypted info in a (MySQL) database you need to use the VARBINARY type for that field

    Reply
  4. Marat

    You would probably need to bin2hex $encrypted and $iv before sending in through HTTP GET or POST

    Reply
  5. chadwalt

    I have aes and it does encrypt but when I use it to decrypt I don’t get the original file.

    Reply
  6. Twodee

    Hey! This post seems great, and it has proved to be so helpful. But there’s one question, I have seen the openssl autogenerated iv and key, and it returns some characters my browser can’t recognize. Should I use something like base64 encode or hash them maybe so that I can get them when I require in plain text?

    Reply
  7. /dev/random

    Good article, thank you.

    Sometimes the decryption function does not work because the initialization vector (IV) contains another “:” so the explode function does not split correctly the encrypted data and the IV. To fix it you just need to add another parameter to the explode function to split at the first occurence of “:”:

    explode(‘:’, $encrypted, 2);

    Reply
  8. Mark

    You’re missing a critical step. When you use openssl_random_pseudo_bytes you should be passing in a second argument. The function populates this argument with true/false indicating whether or not strong cryptography was used. If it is not then your IV may not meet standards and should not be used.

    Reply
  9. Josh

    Thanks for the article!

    Sometimes the decryption function does not work because the initialization vector (IV) contains another “:”

    Replace line 29 with this:
    $parts = explode(‘:’, $encrypted,(mb_substr_count($encrypted, “:”))+1);

    Reply
  10. Mike Harris

    I think its also worth noting that picking a character such as : which *wont* be present in your base64-ed string gives a pretty big clue to anyone who gains illicit access to your data that the portion after the : is some sort of salt or iv.

    Surely its safer to make your iv look exactly like base64 data and just append to the start or end. you always know the length of the iv so you can just substring it off before decrypting the remaining data.

    I’m new to this stuff so apologies if I’m talking rubbish!

    Reply
  11. Jonathan

    @/dev/random
    Exploding on the first occurrence of “:” will not allow you to have a “:” in your encryption data.
    Using base64 encode on both the encryption data and the iv before combining will solve this:

    base64_encode($encrypted) . ‘:’ . base64_encode($iv);

    Reply
  12. Dale

    As a possible workaround for the : issue mentioned above, perhaps a check of the generated hash for the : character and regenerating until one comes up without a : in it would work? Otherwise,

    $parts = explode(‘:’, $encrypted_data);
    $encrypted = array_shift($parts);
    $iv = implode(‘:’, $parts);

    might get you where you want to go even if there was a : in there.

    Reply
  13. Walturburk

    Why not just write the encryption key and iv to a config.ini file on your server, since they always stay the same after being generated.

    The config file is stored in a folder not accessible from the internet.

    No need to re-save the iv for everything encrypted, right?

    Reply
  14. harpej singh

    we have AES encrypted code but i am unable to decrypt them.
    i have also key for them.
    encrypted code in java/jsp and i want to decrypt in PHP.
    how it is possible.
    i am using below code

    function encrypt($data, $key)
    {
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key,$data,MCRYPT_MODE_CBC,”\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0″));
    }
    function decrypt($data, $key)
    {
    $decode = base64_decode($data);
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$key,$decode,MCRYPT_MODE_CBC,”\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0″);
    }

    it working fine if we encrypt our new string, but we have already encrypted code.
    please help me

    harpej singh
    hs_2005@yahoo.co.in
    9214311070

    Reply
  15. clem

    Great but I am encountering error below using the very same code above:

    Warning: openssl_decrypt(): IV passed is only 8 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0 in C:\xampp\htdocs\aes256cbc.php on line 26
    Decrypted: Encrypt W�S�?��:se!

    Reply
  16. Billy

    About “:” problem >> use “separator” instead of “:”. That’s it.

    $separator = “separator”;
    $encrypted = $encrypted . $separator . $iv;
    explode($separator, $encrypted);

    Reply
    • Cocksworth McJohnson

      The reason for the “:” is that u wont notice that its a separator when looking at the encrypted text. The word “separator” makes this whole dealio kinda obvious.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *