By maTu


2012-04-03 13:06:30 8 Comments

currently we have a mcrypt implentation on our systems to crypt some sensible data in our PHP application. Now we have a new requirement that we have to change the crypt module to openssl. Another thing which is important know is that we are using the cipher blowfish and the mode ecb. So I began to test what are differences and how I can decrypt mcrypt encrypted strings with openssl.

I used the standard PHP function:

  • mcrypt_encrypt vs. openssl_encrypt
  • mcrypt_decrypt vs. openssl_decrypt

Both methods are delivering different results. Second thing is that in the given cipher (blowfish) and mode (ecb) in both types different IV lengthes are required (openssl=0 and mcrypt=56).

Does anybody know how I can easily change the modules without having a big migration effort?

Thanks in advance!

UPDATE:

Here is the code, which I tested it:

<?php 

function say($message){
    if(!is_string($message)){
        if(!isset($_SERVER["HTTP_USER_AGENT"])) echo "<pre>";
        echo var_export($message, true) . ((!isset($_SERVER["HTTP_USER_AGENT"]) ? "\n" : "<br />"));
        if(!isset($_SERVER["HTTP_USER_AGENT"])) echo "</pre>";
    }else{
        echo $message . ((!isset($_SERVER["HTTP_USER_AGENT"]) ? "\n" : "<br />"));
    }
}

say("= Begin raw encryption");
$key    = "anotherpass";
$str    = "does it work";

say("  Params:");
say("  - String to encrypt '".$str."'");
say("  - Key: ".$key);
say("");


$params = array(
    "openssl"  => array(
        "cipher"    => "BF",
        "mode"      => "ECB",
    ),
    "mcrypt" => array(
        "cipher"    => "blowfish", 
        "mode"      => "ecb",
    ),
);

say("= Mcrypt");
$handler = mcrypt_module_open($params['mcrypt']['cipher'], '', $params['mcrypt']['mode'], '');
$iv      = mcrypt_create_iv (mcrypt_enc_get_iv_size($handler), MCRYPT_RAND);
$keysize = mcrypt_enc_get_key_size($handler);
mcrypt_generic_init($handler,$key,"\0\0\0\0\0\0\0\0");
say("  Params:");
say("  - InitVector   ".bin2hex($iv)." (bin2hex)");
say("  - Max keysize  ".$keysize);
say("  - Cipher       ".$params['mcrypt']['cipher']);
say("  - Mode         ".$params['mcrypt']['mode']);
say("");
say("  Encryption:");
$m_encrypted = mcrypt_generic($handler, $str);
$m_decrypted = mdecrypt_generic($handler, $m_encrypted);
say("  - Encrypted   ".bin2hex($m_encrypted)." (bin2hex)");
say("  - Descrypted  ".$m_decrypted);
say("");


say("= Openssl");
say("  Params:");
say("  - InitVector   not needed");
say("  - Max keysize  ".openssl_cipher_iv_length($params['openssl']['cipher']."-".$params['openssl']['mode']));
say("  - Cipher       ".$params['openssl']['cipher']);
say("  - Mode         ".$params['openssl']['mode']);
say("");
say("  Encryption:");
$o_encrypted = openssl_encrypt($str,$params['openssl']['cipher']."-".$params['openssl']['mode'],$key,true);
$o_decrypted = openssl_decrypt($o_encrypted,$params['openssl']['cipher']."-".$params['openssl']['mode'],$key,true);
say("  - Encrypted   ".bin2hex($o_encrypted)." (bin2hex)");
say("  - Descrypted  ".$o_decrypted);

And this is my result:

= Begin raw encryption
  Params:
  - String to encrypt 'does it work'
  - Key: anotherpass

= Mcrypt
  Params:
  - InitVector   06a184909d7bf863 (bin2hex)
  - Max keysize  56
  - Cipher       blowfish
  - Mode         ecb

  Encryption:
  - Encrypted   0e93dce9a6a88e343fe5f90d1307684c (bin2hex)
  - Descrypted  does it work

= Openssl
  Params:
  - InitVector   not needed
  - Max keysize  0
  - Cipher       BF
  - Mode         ECB

  Encryption:
  - Encrypted   213460aade8f9c14d8d51947b8231439 (bin2hex)
  - Descrypted  does it work

Maybe any ideas now?

Thanks!

4 comments

@daiwai 2018-07-08 16:22:13

In case you want to encrypt with openssl and still get the same result as if you had encrypted it with mcrypt when decrypting with mcrypt, you need to manually null-pad the input string prior to encrypting it with openssl_encrypt and pass the OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING options.

$str = 'encrypt me';
$cipher = 'AES-256-CBC';
$key = '01234567890123456789012345678901';
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
$iv_len = 16;
$str_len = mb_strlen($str, '8bit');
$pad_len = $iv_len - ($str_len % $iv_len);
$str .= str_repeat(chr(0), $pad_len);
$iv = openssl_random_pseudo_bytes($iv_len);


$encrypted = openssl_encrypt($str, $cipher, $key, $opts, $iv);

Decrypting with mcrypt_decrypt will then work just as if you had also used mcrypt for encryption.

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv)

@ColinM 2018-03-09 17:32:09

@clover is right that the default padding for Blowfish is different between mcrypt and Openssl, but is wrong that it can't be done. If you use the OPENSSL_ZERO_PADDING option for the decrypt the two actually are compatible:

openssl_decrypt($data, 'bf-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);

@zaph 2018-03-09 22:58:02

OPENSSL_ZERO_PADDING does not add any padding, any non-standard padding will need to be added manually prior to encryption and removed on decryption. From the docs openssl_encrypt, perhaps you are using a different version. From the doc comments: So, OPENSSL_ZERO_PADDING disables padding for the context, which means that you will have to manually apply your own padding out to the block size. Without using OPENSSL_ZERO_PADDING, you will automatically get PKCS#7 padding.

@ColinM 2018-03-13 07:51:09

@zaph Maybe you should try the code before you downvote? Works for me.

@shawn 2018-03-08 11:52:03

For shorter keys, you should make cycled keys for openssl when migrating mcrypt's blowfish.

function make_openssl_blowfish_key($key)
{
    if("$key" === '')
        return $key;

    $len = (16+2) * 4;
    while(strlen($key) < $len) {
        $key .= $key;
    }
    $key = substr($key, 0, $len);
    return $key;
}

See: https://bugs.php.net/bug.php?id=72362

See: Moving from mcrypt with Blowfish & ECB to OpenSSL

@clover 2013-11-03 00:23:10

Blowfish is the block cipher. It requires the data to be padded before encryption. OpenSSL uses PKCS#7 and mcrypt uses PKCS#5. Different padding algorythms for data. Minimal PKCS#5 padding length is 0, for PKCS#7 it's 1 (wikipedia). Take a look at this example (i've manually padded input data for mcrypt_encrypt() in PKCS#7 style):

<?php 

$key = "anotherpassword1";
$str = "does it work 12";

$enc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $str."\1", MCRYPT_MODE_ECB);
$dec = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $enc, MCRYPT_MODE_ECB);
echo(bin2hex($enc).PHP_EOL);
var_dump($dec);

$enc = openssl_encrypt($str, 'bf-ecb', $key, true);
$dec = openssl_decrypt($enc, 'bf-ecb', $key, true);
echo(bin2hex($enc).PHP_EOL);
var_dump($dec);

?>

It's impossible to openssl_decrypt() data encrypted with mcrypt_encrypt(), unless manual data padding was made with PKCS#7 before mcrypt_encrypt() was called.

There is only one way in your case - recrypt the data.

PS: There is an error in your source - ECB mode does not uses IV at all (wikipedia)

@Narf 2014-01-30 13:08:25

Thanks, this hint helped me for making Rijndael-128/AES-128 compatible between MCrypt and OpenSSL.Unfortunately though, it doesn't work for Blowfish -

@Narf 2014-02-05 12:14:57

Turns out it does work for Blowfish, BUT the key size must be at least 16 bytes (Wikipedia article for Blowfish says it supports less).

@clover 2014-02-09 18:41:00

It's because of padding in PKCS#7

@Narf 2014-02-10 05:23:26

Padding is applied to the plain-text data, I'm talking about the key. Regardless, +1 for a useful answer. :)

@jww 2017-02-16 15:29:06

@Narf - It sounds like a bug if a Blowfish key of 4 bytes is rejected since the keysize is 32-bits to 448-bits.

@zaph 2018-03-09 22:51:39

@clover PKCS#7 and PKCS#5 are essentially the same and are the same in implementation, it is just that lazy developers re-used the PKCS#5 identifier instead of add a PKCS#7 identifier. Please fix the answer and comment. See PKCS#7 padding: PKCS#5 padding is identical to PKCS#7 padding, except that it has only been defined for block ciphers that use a 64-bit (8-byte) block size. In practice the two can be used interchangeably.

@zaph 2018-03-09 22:55:05

@clover Also see: What is the difference between PKCS#5 padding and PKCS#7 padding: Many cryptographic libraries use an identifier indicating PKCS#5 or PKCS#7 to define the same padding mechanism.

Related Questions

Sponsored Content

14 Answered Questions

[SOLVED] How to create a self-signed certificate with OpenSSL

22 Answered Questions

[SOLVED] Laravel requires the Mcrypt PHP extension

7 Answered Questions

[SOLVED] Using OpenSSL what does "unable to write 'random state'" mean?

  • 2008-09-18 16:56:43
  • Luke Francl
  • 232887 View
  • 397 Score
  • 7 Answer
  • Tags:   openssl

1 Answered Questions

[SOLVED] Upgrading my encryption library from Mcrypt to OpenSSL

1 Answered Questions

[SOLVED] OpenSSL or Mcrypt? (openssl_encrypt or mcrypt_encrypt)

  • 2016-04-12 11:18:51
  • user1921724
  • 4002 View
  • 10 Score
  • 1 Answer
  • Tags:   php openssl mcrypt

2 Answered Questions

[SOLVED] AES encryption differences between php mcrypt and a Delphi component

0 Answered Questions

How to configure Apache to use a specific cipher mode in SSL mode?

1 Answered Questions

[SOLVED] mcrypt_generic_init: Meaning of the iv parameter

2 Answered Questions

Sponsored Content