2

I'm trying to decrypt a file, I can decrypt it using OpenSSL in the terminal using the following string.

openssl -enc -d -aes-192-ecb -in file.crypt -out file -K 0123456789abcdef -iv 0

However, I want to decrypt this file in PHP. I have the following code:

$file = file_get_contents('file.crypt');
$key = 0123456789abcdef;
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_192, $key, $file, MCRYPT_MODE_ECB);

print_r($data);

Obviously I'm missing something as the PHP script is returning data, but not the plain-text.

I have tried using MCRYPT_RIJNDAEL_128 but with no luck. Please let me know if you can see what I'm doing wrong. Thanks in advance.

UPDATE


I have successfully decrypted my file using the following:

$key = pack('H*', '0123456789abcdef'); //In >= PHP 5.4 you can use hex2bin() I think.
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $file, MCRYPT_MODE_ECB);
James Whayman
  • 338
  • 3
  • 10
  • Does `$file` actually contain the encrypted contents of the file? – Mike Jan 12 '14 at 00:27
  • Can you show how you're encrypting the file, so that we can reproduce? – Drew Jan 12 '14 at 00:30
  • What if you try using 0 as the 5th parameter to mcrypt_decrypt? `mcrypt_decrypt(MCRYPT_RIJNDAEL_192, $key, $file, MCRYPT_MODE_ECB, 0);` – Mike Jan 12 '14 at 00:33
  • @Mike: the file.crypt is an encrypted sqlite database. Using 0 as the fifth parameter causes the mcrypt_decrypt function to return false. – James Whayman Jan 12 '14 at 00:44
  • @Drew: I am not encrypting the file, it's encrypted by an application which I don't have white-box access to. – James Whayman Jan 12 '14 at 00:44
  • @Epiphron according to [here](http://www.openssl.org/docs/apps/enc.html) the IV must be a string, so maybe try `"0"`. – Mike Jan 12 '14 at 00:47
  • @Epiphron and you misunderstood my first question. Does the PHP variable `$file` actually contain the contents of that file? I'm just trying to determine whether there was any sort of read error. – Mike Jan 12 '14 at 00:48
  • @Mike, thanks for your ideas. I have tried replacing the IV with 0 previously, apologies I forgot to mention it in my post (it returns false). Yes, the `$file` variable contains the correct data, I have just checked. – James Whayman Jan 12 '14 at 00:55
  • I don't see how that's even possible unless there is an error in the manual page for mcrypt_decrypt because `FALSE` is not one of the [possible return values](http://www.php.net/manual/en/function.mcrypt-decrypt.php#refsect1-function.mcrypt-decrypt-returnvalues). – Mike Jan 12 '14 at 00:57
  • @Mike, yes you're correct. It actually generates an error `mcrypt_decrypt - [internal], line ??` – James Whayman Jan 12 '14 at 01:08
  • Possible duplicate of [Decrypting data with openssl commandline tool](http://stackoverflow.com/questions/11160788/decrypting-data-with-openssl-commandline-tool). The cited question uses `PHP`, `mcrypt` and `openssl` command line tool. – jww Jan 12 '14 at 01:15
  • @noloader, thanks for the link. I've reviewed the linked question and it doesn't duplicate this one. – James Whayman Jan 12 '14 at 02:02

2 Answers2

4

The keys and IV in openssl are in hexadecimals (and therefore too short) and those in PHP are used as character values. Please specify 32, 48 or 64 hexadecimal digits for AES keys in openssl and the same value in 16, 24 or 32 bytes in PHP. The IV should always be 32 hex digits or 16 bytes as that is the block size of AES.

You should always use MCRYPT_RIJNDAEL_128 as the 128 in there is the block size - not the key size - of the algorithm, The MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 algorithms do not implement AES.

Also, openssl defaults to PKCS#7 padding, check the mcrypt_encrypt comments section for implementation of PKCS padding for PHP - it does not provide it by default.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    "The MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 algorithms do not implement AES". +1. Lots of people don't realize that and interchange AES and Rijndael. – jww Jan 12 '14 at 11:52
  • @owlstead Thank you very much! I modified my PHP code using your answers and I have successfully decrypted the file. I have edited my original question to include the changes. – James Whayman Jan 12 '14 at 13:35
  • Glad it worked! Please note that the 8 byte key is still short. It may be that the key is extended with `00` valued bytes - it certainly is with PHP mcrypt - but such keys are not strong enough for AES-128 keys (16 hex characters equals 8 bytes, or 64 bit, half of the keysize of AES-128). – Maarten Bodewes Jan 12 '14 at 17:02
  • @noloader The initial sample of `mcrypt_encrypt` used `MCRYPT_RIJNDAEL_256` among many other errors. So I filed a bug report and changed the official sample code :) A lot less questions about that now... – Maarten Bodewes Jan 12 '14 at 22:09
1

openssl -enc -d -aes-192-ecb -in file.crypt -out file -K 0123456789abcdef -iv 0

Specify your key in a keyfile with -kfile, and not on the command line with -K.


Maybe I'm reading the sources of <openssl dir>/apps/enc.c wrong, but it looks broke to me when using the -K option.

First, there is a declaration for hex encoded values from the command line (line 114):

char *hkey=NULL,*hiv=NULL,*hsalt = NULL;

Next, hkey is populated from the command line (line 265);

else if (strcmp(*argv,"-K") == 0)
{
    if (--argc < 1) goto bad;
    hkey= *(++argv);
}

Then, a few tests are performed. First, line 422:

if ((str == NULL) && (cipher != NULL) && (hkey == NULL))
{
    ...
    EVP_read_pw_string(...)
    ...
}

And then line 581:

if ((hkey != NULL) && !set_hex(hkey,key,sizeof key))
{
    BIO_printf(bio_err,"invalid hex key value\n");
    goto end;
}

Then nothing else is done with hkey.


Now, following the kfile option looks much more interesting:

else if (strcmp(*argv,"-kfile") == 0)
{
    static char buf[128];
    FILE *infile;
    char *file;
    /* lots of reading and parsing removed */
    ...
    str=buf;
}

After the key is assigned to str, something is done with it:

if (cipher != NULL)
{
    /* Note that str is NULL if a key was passed on the command
     * line, so we get no salt in that case. Is this a bug?
     */
    if (str != NULL)
    {
        /* Salt handling: if encrypting generate a salt and
         * write to output BIO. If decrypting read salt from
         * input BIO.
         */
        unsigned char *sptr;
        if(nosalt) sptr = NULL;
        else
        {
            if(enc) {
                if(hsalt) {
                    if(!set_hex(hsalt,salt,sizeof salt)) {
                        BIO_printf(bio_err, "invalid hex salt value\n");
                        goto end;
                    }
                } else if (RAND_pseudo_bytes(salt, sizeof salt) < 0)
                    goto end;
                ...
                EVP_BytesToKey(cipher,dgst,sptr,
                              (unsigned char *)str,
                              strlen(str),1,key,iv);

Here's a small debugging session with a write watchpoint on key. Its not written when using the -K option (though it is used in some EVP_* functions):

(gdb) b main
Breakpoint 1 at 0x1000071c0: file enc.c, line 106.
(gdb) watch key@16
Hardware watchpoint 3: {<data variable, no debug info>} 140735109990496 @ 16
(gdb) r -d -aes-192-ecb -in file.crypt -out file.txt -K 0123456789abcdef -iv 0
Starting program: .../openssl-1.0.1e/apps/enc.exe -d -aes-192-ecb -in file.crypt
-out apps.c -K 0123456789abcdef -iv 0

Breakpoint 1, main (argc=11, argv=0x7fff5fbff970) at enc.c:106
106         char *strbuf=NULL;
(gdb) c
Continuing.

Program exited normally.

Just bike shedding here: aes-192-ecb and MCRYPT_MODE_ECB is only secure if you don't reuse keys and the file is 16 bytes or less. As soon as you reuse the key or exceed 16 bytes, you loose PRP-security.

jww
  • 97,681
  • 90
  • 411
  • 885