For the last couple of days, I have been learning a bit about encrypting data in PHP, as I need a good encryption/decryption function for storing users' passwords in a DB. I use openssl_random_pseudo_bytes() for the IV, hash_hmac with SHA256 and openssl_encrypt(). Here's the function for encryption:

// 32 bytes (256-bit) (64 char) encryption key
define('ENCRYPTION_KEY', 'd027e7*+7b6d5fcd55f4b5c32611b87cd923e88837b63bf2941ef819dc8ca282');
// 16 bytes (128-bit) (32 char) encryption key
define('ENCRYPTION_KEY2', 'b6d5fcd55f4b5c32611b87cd923e88ae');
function pw_encrypt($encrypt, $key, $type){

    $hmac_key = 'd027e7*+7b6d5fcd55f4b57su921b87cd923e88837b63bf2941ef819dc8ca282';

    $encrypt = serialize($encrypt);

    $method = "AES-$type-CBC";

    $isCryptoStrong = false; // Will be set to true by the function if the algorithm used was cryptographically secure
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method), $isCryptoStrong);

        throw new Exception("Non-cryptographically strong algorithm used for iv generation. This IV is not safe to use.");

    $mac = hash_hmac('sha256', $encrypt, $hmac_key);

    $passcrypt = openssl_encrypt($encrypt.$mac,$method,$key,OPENSSL_RAW_DATA,$iv);
    $encoded = base64_encode($passcrypt).'|'.base64_encode($iv);

    return $encoded;

So I've run this bit of code to test AES-128/-256 with 128 bit / 256 bit encryption keys. Now the thing that bothers me that AES-256 seems faster than AES-128, is that normal?

Here are the results-- times are usually something similar to this:

$data = '&>tsX@(3BjsAepZW';
Encrypted Data AES-128 128bit key: Length: 153 Total Execution Time: 2.6027361551921E-6 Mins
Encrypted Data AES-128 256bit key: Length: 153 Total Execution Time: 1.8000602722168E-6 Mins
Encrypted Data AES-256 128bit key: Length: 153 Total Execution Time: 1.7166137695313E-6 Mins
Encrypted Data AES-256 256bit key: Length: 153 Total Execution Time: 1.7166137695313E-6 Mins

So, what bugs me:

  1. Am I doing something wrong here, or is AES-128 really slower than AES-256?
  2. How come the length of encoded string is in all 4 cases the same?
  3. Does the encryption key have to be in HEX format or can I use *+/-_.,'?
  4. The key for has_hmac, should it be randomly generated or just as I have it, predefined? If random, what's the best pratice for creating a random key? And what length and type should it be (hex or +*/-'.?)?

2 Answers 2


I'll answer in order:

  1. No, you may not be doing something wrong. This is just the compiler warming up and performing optimizations. The second run you see that AES-128 is already about as fast, which is what you would expect (it should be ever so slightly faster in the end, but that might not even be noticeable).

  2. Don't know, you'd have to debug, because that's certainly wrong.

  3. The key should not be in any kind of encoding. The key used by OpenSSL should consist of bytes indistinguishable from random. You can use hexadecimals for storing keys in string (something that's rather questionable as well) but you should decode it before using it.

  4. Same as for the encryption key; you'd use openssl_random_pseudo_bytes or another secure random number generator. Then you may encode it to hexadecimals or base 64, as long as you decode it again to binary before you use it.

Note that if you provide a 16 byte key as 32 hexadecimals that you might be using AES-256 without realizing it yourself. Best test against another framework / crypto library (but NOT mcrypt).

You should not store your users passwords encrypted. Instead you should use a password hash such as bcrypt.

  • $\begingroup$ Basically, with your current level of knowledge, you should not encrypt passwords, period. $\endgroup$
    – Maarten Bodewes
    Commented Feb 19, 2016 at 11:06
  • $\begingroup$ Thanks so much for the answer. I've started looking into password hasing, would you say this function is any good? $random = openssl_random_pseudo_bytes(18); $salt = sprintf('$2y$%02d$%s', 13, // 2^n cost factor substr(strtr(base64_encode($random), '+', '.'), 0, 22) ) $hash = crypt($password, $salt) $given_hash = crypt($given_password, $db_hash) function isEqual($str1, $str2){ $n1 = strlen($str1); if (strlen($str2) != $n1) { return false; } for ($i = 0, $diff = 0; $i != $n1; ++$i) { $diff |= ord($str1[$i]) ^ ord($str2[$i]); } return !$diff; } $\endgroup$
    – ikatic
    Commented Feb 19, 2016 at 13:09
  • $\begingroup$ It goes a long way with the salt and the time-constant verification, but I don't see how it includes the cost factor in the calculations. It could use bcrypt underneath, but I cannot verify that given the code above. I'm also not 100% if ord itself is actually time-constant. $\endgroup$
    – Maarten Bodewes
    Commented Feb 19, 2016 at 13:58

With PHP's openssl_encrypt the key length is extended to the length of the key given. So even if you select "AES-128-CBC", by passing a 256-bit key you will get AES-256. (Doing the opposite you get a zero-extended key.)

Therefore, all but the first are actually testing AES-256 (and even it may be, if you used a hex encoded key input). As for why the first is slower, could be anything from caches warming up to an actual difference. More likely I'd suspect that your timing method is not accurate enough – you could try over much longer data.

There is no good reason AES-256 would be faster than AES-128, since the only differences are in key schedule and the number of rounds. But it is possible that for whatever reason your OpenSSL has a better optimized version of AES-256 than AES-128.

  • $\begingroup$ Ah, you'd hope that they would not make the same mistakes as the mcrypt library. Extending the key with zeros...sigh. $\endgroup$
    – Maarten Bodewes
    Commented Feb 19, 2016 at 11:09
  • $\begingroup$ @MaartenBodewes, yeah, crypto is one area where "be liberal in what you accept..." is not a good maxim. $\endgroup$
    – otus
    Commented Feb 19, 2016 at 11:10
  • 1
    $\begingroup$ @otus Lenient validation is a bad idea in most areas. What you really want are well defined extension points. (If you read the original text, it can even be interpreted as arguing for extension points, not sloppy validation) $\endgroup$ Commented Feb 19, 2016 at 12:33
  • $\begingroup$ @CodesInChaos, true, it just tends to be quoted often and with little context. $\endgroup$
    – otus
    Commented Feb 19, 2016 at 13:08
  • $\begingroup$ Thanks for the answer! So basically if I understood correctly if I put short key, ex. 64bit for AES128 he will just add zeros to the end to create itself a 128 bit key? $\endgroup$
    – ikatic
    Commented Feb 19, 2016 at 13:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.