32-character PHP AES Key for mcrypt_encrypt

3k views Asked by At

Consider the following PHP code:

<?php
$key = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
                            $key,
                            $data,
                            MCRYPT_MODE_CBC,
                            $iv);

print "Encoded1: " . base64_encode($encrypted) . "\n";

$key = "12345678123456781234567812345678";

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
                            $key,
                            $data,
                            MCRYPT_MODE_CBC,
                            $iv);

print "Encoded2: " . base64_encode($encrypted) . "\n";

When run, this produces the output:

Encoded1: iz1qFlQJfs6Ycp+gcc2z4w==
Encoded2: n3D26h/m8CSH0CE+z6okkw==

Note that I stole the first bit of code from PHP Java AES CBC Encryption Different Results

Now - here's the question:

In the first case, the key that was passed in was a string of 16 characters. If each of the individual characters was interpreted as an 8-bit quantity, this gives the 128-bit key size that one would expect. Indeed, the Java code that's on the StackOverflow page that I referenced above does exactly that, and obtains the same result as the PHP.

In the second call to mcrypt_encrypt above, I have doubled the length of the key. mcrypt_encrypt accepts this happily, but produces a different encrypted output than in the first case. Clearly, therefore, it considers this a different key - it does not, for example take only the first 128 bits and discard any past that.

So, how does mcrypt_encrypt process the input key string to come up with the 128-bit key that the MCRYPT_RIJNDAEL_128 algorithm requires?

If it makes any difference, the case I'm specifically interested in is when a 32-character string is passed in like my second example - I have to create a matching decryption routine (in Java), so I need to figure out how the key is actually generated in this case. The page I cited has perfectly-good Java code (which works with all my test cases) - I'm just missing the proper set of key bytes.

1

There are 1 answers

1
Artjom B. On

There are two important parameters for the Rijndael algorithm. There is the key size (128-bit, 192-bit and 256-bit) and then there is the block size (128-bit, 192-bit and 256-bit). The 128 in MCRYPT_RIJNDAEL_128 refers to the block size. The key size is variable.

When you pass keys of different lengths into MCrypt, it will select the appropriate key size automatically, so you don't and can't set it. MCRYPT_RIJNDAEL_128 is AES (AES-128, AES-192, AES-256). MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 are not AES anymore.

If the Java code produced a matching result for the 128-bit key, then it will produce a matching result for the 256-bit key as well.

MCrypt is a little strange. Before PHP version 5.6.0 it would take any key length and not just 128-bit, 192-bit or 256-bit. The key would be filled up with 0x00 bytes up to the next valid key length.


Since Java doesn't support ZeroPadding out of the box, you should use a proper padding scheme such as PKCS#5/PKCS#7 padding in PHP. This answer has a very good implementation of it.