0

I need to be able to decrypt values using OpenSSL that were generated using Mcrypt under PHP.

I have this working, with the exception that the key used to encrypt them was ascii.

The following is my code that demonstrates a working case where OpenSSL can decrypt the value encrypted with Mcrypt, when the key is an MD5.

<?php
$message = 'test';
$key = md5('Quigibo');

$iv = openssl_random_pseudo_bytes(0);

$encrypted = mcrypt_encrypt(
    MCRYPT_BLOWFISH,
    $key,
    $message,
    MCRYPT_MODE_ECB,
    $iv
);

$decrypted = openssl_decrypt(
    $encrypted,
    'bf-ecb',
    $key,
    OPENSSL_RAW_DATA | OPENSSL_NO_PADDING,
    $iv
);

$trimDecrypted = rtrim($decrypted);

var_export(
    [
        'Original message' => $message,
        'Encrypted' => bin2hex($encrypted),
        'Decrypted' => $decrypted,
        'Trim decrypted' => $trimDecrypted,
        'Message length' => mb_strlen($message, '8bit'),
        'Decrypted length' => mb_strlen($decrypted, '8bit'),
        'Message == decrypted' => $message === $trimDecrypted
    ]
);

However, if you change $key to be the value "Quigibo" (as opposed to an MD5 hash of that value) the encrypted value cannot be decoded with OpenSSL.

Is there a form of encoding I can apply to the ASCII key prior to use with OpenSSL such that it will correctly decrypt the value?

Courtney Miles
  • 3,756
  • 3
  • 29
  • 47
  • 1
    If they key length is too short mcrypt pads it with nulls (depending on the encryption type). Try to use a longer ASCII key and see if that works. If it does, then this is the issue and is a difference between the libraries. – Sami Kuhmonen Aug 10 '17 at 06:34
  • Also see [Upgrading my encryption library from Mcrypt to OpenSSL](http://stackoverflow.com/q/43329513/608639), [Replace Mcrypt with OpenSSL](http://stackoverflow.com/q/9993909/608639) and [Preparing for removal of Mcrypt in PHP 7.2](http://stackoverflow.com/q/42696657/608639) – jww Aug 10 '17 at 10:52
  • See also https://stackoverflow.com/questions/41181905/php-mcrypt-encrypt-to-openssl-encrypt-and-openssl-zero-padding-problems, where trying to encrypt with OpenSSL in an equivalent way to Mcrypt and null padding is also a problem. – Courtney Miles Aug 14 '17 at 01:24

1 Answers1

2

Revised answer:

There is a bug in OpenSSL which null pads the key to 16 bytes for Blowfish, which is incorrect because the cipher supports a variable length key. They have very recently added a flag to fix this - OPENSSL_DONT_ZERO_PAD_KEY - but it is only available in php 7.1.8 which was released a little over a week ago... Relevant bug: https://bugs.php.net/bug.php?id=72362

A workaround is to manually cycle the key and feed that into OpenSSL:

$keylen = (floor(mb_strlen($key, '8bit')/8)+1)*56;
$key_cycled = substr(str_repeat($key,ceil($keylen/strlen($key))),0,$keylen);

$decrypted = openssl_decrypt(
$encrypted,
'bf-ecb',
$key_cycled,
OPENSSL_RAW_DATA | OPENSSL_NO_PADDING,
$iv
);
Ed Bordin
  • 548
  • 3
  • 10
  • Using a key that is 16 characters or more will work. Unfortunately my key is less. If I pad the short key with `"\0"` it does not work (`str_pad($key, $pad, chr(0)))`) I have tried a full range of padding values without success, which makes me think that it's either not padded, or padded with something other than null. – Courtney Miles Aug 11 '17 at 06:32
  • Which version of mcrypt are you using? If you are using a version that is too new then it will return FALSE and refuse to encrypt the data at all in your test code above. – Ed Bordin Aug 11 '17 at 06:38
  • Maybe adding OPENSSL_DONT_ZERO_PAD_KEY will help you to force OpenSSL to use the exact key size (relevant: https://bugs.php.net/bug.php?id=72362, https://bugs.php.net/bug.php?id=71917) Just found this SO post which suggests that mcrypt didn't have the key-padding behaviour for blowfish because it supports variable length keys: https://stackoverflow.com/questions/16331928/how-long-is-the-default-key-size-used-by-mcrypt-blowfish – Ed Bordin Aug 11 '17 at 06:52
  • 1
    Unfortunately OPENSSL_DONT_ZERO_PAD_KEY is only in php 7.1.8 which was released a little over a week ago... I just did a quick test and confirmed that if I encrypt using mcrypt with a key null-padded to 16 bytes and then decrypt with openssl with the unpadded key, it works. – Ed Bordin Aug 11 '17 at 07:26
  • I can confirm that openssl will automatically pad the key, where as mcrypt does not pad. Which means it's not not possible to decrypt with openssl when the key is less than 16 bytes until `OPENSSL_DONT_ZERO_PAD_KEY` is released. If you revise your answer with these specifics, I will be happy to mark it as the accepted answer. I would not have figured this out on my own and appreciate your effort. – Courtney Miles Aug 13 '17 at 23:49
  • Thanks! I was keen to get my rep up from basically zero so I was pushing extra hard to find an answer ;) See above, I've found a workaround that should work (perhaps by now you've arrived at a similar solution anyway). – Ed Bordin Aug 14 '17 at 00:42
  • `$keyLen` will need to be different for different length keys. The following can be used to dynamically calculate what it needs to be: `(floor(mb_strlen($key, '8bit')/8)+1)*56` – Courtney Miles Aug 14 '17 at 03:13
  • Ah right, I missed that detail. I've edited the answer to incorporate that formula so that the workaround is usable for anyone else that stumbles on this issue! – Ed Bordin Aug 14 '17 at 05:24