4

I have updated my php version to 7.1. I had functions where i encrypt data using mcrypt. Now this function is deprecated.

How can i decrypt the data anyway withoud going back to older versions of php.

This is the code i used:

public function encrypt($plaintext) {
    $ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
    $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
    $ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
    return base64_encode($iv.$ciphertext);
}

public function decrypt($ciphertext) {
    $ciphertext = base64_decode($ciphertext);
    $ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
    if (strlen($ciphertext) < $ivSize) {
        throw new Exception('Missing initialization vector');
    }

    $iv = substr($ciphertext, 0, $ivSize);
    $ciphertext = substr($ciphertext, $ivSize);
    $plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
    return rtrim($plaintext, "\0");
}

With Constants:

const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE   = MCRYPT_MODE_CBC;

I saw that it was recommended to use OpenSSL. That is what i will use from now on. But how can i decrypt the older data using this method?

Thanks

Edit: I know i can use OpenSSL as alternative. Thats what i am doing for the content from now on. But i need to decrypt my mcrypted code from my old contents.

*Edit request @symcbean

Tried to decrypt with OpenSSL like this:

public function decrypt($ciphertext) {
    $ciphertext = base64_decode($ciphertext);

    if (!function_exists("openssl_decrypt")) {
       throw new Exception("aesDecrypt needs openssl php module.");
    }

$key    = $this->key;
$method = 'AES-256-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv     = substr($ciphertext,0,$ivSize);
$data   = substr($ciphertext,$ivSize);
$clear  = openssl_decrypt ($data, $method, $key, 'OPENSSL_RAW_DATA'|'OPENSSL_ZERO_PADDING', $iv);

return $clear;
}
Puya Sarmidani
  • 1,226
  • 9
  • 26
  • Possible duplicate of [PHP 7 - mcrypt deprecated, need alternative](http://stackoverflow.com/questions/41272257/php-7-mcrypt-deprecated-need-alternative) – Ben Feb 22 '17 at 11:39
  • 1
    Its not. I do not need a alternative. I know i can use openssl. But i need to decrypt my mcrypted content. – Puya Sarmidani Feb 22 '17 at 11:41
  • 4
    @Ben: disagree - that's a discussion about passwords and the accepted answer does not address the specific issue of decryption. – symcbean Feb 22 '17 at 11:57
  • Have you tried to decrypt the mcrypt ciphertext with openssl? Where is your code for this? What happenned? – symcbean Feb 22 '17 at 11:58
  • i tried, it didn't result anything. I will try again and edit my post. – Puya Sarmidani Feb 22 '17 at 12:01
  • @symcbean: just edited my post – Puya Sarmidani Feb 22 '17 at 12:07
  • 1
    I think your `$method` should be `"AES-128-CBC"` if you've used `MCRYPT_RIJNDAEL_128` previously. Also `openssl_decrypt` flags are constants, not string, so remove the quotes like `OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING`. – Joe Feb 22 '17 at 12:27
  • Thats a good one. I will try that. @joe – Puya Sarmidani Feb 22 '17 at 12:35

3 Answers3

2

Important thing to note is that mcrypt_encrypt zero-pads input data if it's not a multiple of the blocksize. This leads to ambiguous results if the data itself has trailing zeroes.

openssl_decrypt doesn't remove the zero-padding automatically, so you're left only with the possibility of trimming the trailing nulls.

Here's a trivial example:

$data = "Lorem ipsum";
$key = "1234567890abcdef";
$iv = "1234567890abcdef";

$encrypted = mcrypt_encrypt(
    MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo bin2hex($encrypted) . "\n";

$decrypted = openssl_decrypt(
    $encrypted, "AES-128-CBC", $key,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo var_export($decrypted, true) . "\n";
$result = rtrim($decrypted, "\0");
echo var_export($result, true) . "\n";

Output:

70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . ''
'Lorem ipsum'
Joe
  • 1,656
  • 11
  • 10
0

I solved it. Don't know if its the right way (guess not) But connected remotely on a server with a lower php version. Decrypted all the content and encrypted with OpenSSL.

Thanks for the suggestions!

Puya Sarmidani
  • 1,226
  • 9
  • 26
-1

I also had some problems decrypting data encrypted with mcrypt_encrypt with openssl_decrypt. The following small test encrypts a string with mcrypt and openssl (with added zero padding and without) and decrypts all strings with both methods. This example uses ECB mode but you can easily change this to CBC by adding an IV if needed.

// Setup key and test data
$key = hash("sha256", 'test', true);
$data = 'Hello World';
$enc = $dec = [];
// Encrypt with MCRYPT_RIJNDAEL_128 method
$enc['RIJ'] = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB));
// Encrypt with OpenSSL equivalent AES-256
$enc['AES'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
// Encrypt with OpenSSL equivalent AES-256 and added zero padding
if (strlen($data) % 8) $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
$enc['AES0'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
// Decrypt all strings with MCRYPT_RIJNDAEL_128
$dec['mRIJ'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['RIJ']), MCRYPT_MODE_ECB));
$dec['mAES'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES']), MCRYPT_MODE_ECB));
$dec['mAES0'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES0']), MCRYPT_MODE_ECB));
// Decrypt all strings with OpenSSL equivalent AES-256
$dec['oRIJ'] = bin2hex(openssl_decrypt(base64_decode($enc['RIJ']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
$dec['oAES'] = bin2hex(openssl_decrypt(base64_decode($enc['AES']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
$dec['oAES0'] = bin2hex(openssl_decrypt(base64_decode($enc['AES0']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
// Print results
print_r($enc);
var_dump($dec);

The print_r and var_dump output is the following:

Array
(
    [RIJ] => YcvcTwAMLUMBCZXu5XqoEw==
    [AES] => +AXMBwkWlgM1YDieGgekSg==
    [AES0] => YcvcTwAMLUMBCZXu5XqoEw==
)
array(6) {
  ["mRIJ"]=>
  string(32) "48656c6c6f20576f726c640000000000"
  ["mAES"]=>
  string(32) "48656c6c6f20576f726c640505050505"
  ["mAES0"]=>
  string(32) "48656c6c6f20576f726c640000000000"
  ["oRIJ"]=>
  string(32) "48656c6c6f20576f726c640000000000"
  ["oAES"]=>
  string(22) "48656c6c6f20576f726c64"
  ["oAES0"]=>
  string(32) "48656c6c6f20576f726c640000000000"
}

If you need the same encrypted string with the openssl methods as you had with mcrypt, you'll have add the zero padding manually to the string (AES0 in the example). This way you'll get the exact same encrypted and decrypted strings as before. For some additional information about the zero padding, you should look at Joe's answer here: php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems

If you don't want to manually add the zero padding to all new messages, you'll need different flags for decrypting the old mcrypt-encrypted messages and the new messages encrypted with openssl. For the old messages you'll have to use the OPENSSL_ZERO_PADDING flag ($dec['oRIJ'] in the example), whereas you must not use it for the openssl encrypted messages ($dec['oAES'] in the example). In my case I used this approach, because the default behaviour of openssl seems more correct to me as the mcrypt one - if you encrypt a string with 11 bytes you get a string with 11 bytes back after decrypting it. As you can see in the example, this is not the case with mcrypt or with openssl and the added zero padding. In these cases you would have to remove the trailing zeros manually to get the original data back.

Konrad
  • 1,285
  • 21
  • 28
  • 1. The question is using CBC mode with an IV, this answer does not and thus is not answering the question. 2. *"the encrypted string is different for both methods"* is an error, with the same inputs the encrypted data should be the same. – zaph Jan 03 '18 at 15:44
  • 1) The question was "How to decrypt after Mcrypt deprecation?" which this answer refers to. The example could easily be adjusted to any specific mode. 2) The reason for this is the missing zero-padding which Joe's answer already explained. Also the question was about decryption, my additional information about encryption was just "additional". – Konrad Jan 03 '18 at 16:46
  • Consider upgrading the answer to address these points in the code. – zaph Jan 03 '18 at 20:53