mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems
Asked Answered
S

2

12

I have this mcrypt_encrypt call, for a given $key, $message and $iv:

$string = mcrypt_encrypt(MCRYPT_3DES, $key, $message, MCRYPT_MODE_CBC, $iv);

I'd like to change the mcrypt_encrypt call to an openssl_encrypt one, to future-proof this.

By having $mode = 'des-ede3-cbc' or $mode = '3DES'; and $options = true I get the more similar response, but not identical. Is there other way to call it to get a perfect match?

I am getting this (base64_encoded) for a lorem-ipsum $message+$key combinations, so I am starting to believe one function or the other are padding somewhat the message before encrypting...

for mcrypt:

"Y+JgMBdfI7ZYY3M9lJXCtb5Vgu+rWvLBfjug2GLX7uo="

for for openssl:

"Y+JgMBdfI7ZYY3M9lJXCtb5Vgu+rWvLBvte4swdttHY="

Tried using $options to pass OPENSSL_ZERO_PADDING, but passing anything but 1 (OPENSSL_RAW_DATA, or true) results in an empty string ...

Neither using OPENSSL_ZERO_PADDING nor OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING work... :( I'm using "OpenSSL 1.0.2g 1 Mar 2016".

Already read this q&a, but it does not help me. Not the only one with padding troubles, but no solution in sight so far. Second answer talks about adding padding to mcrypt call, I would really want to remove padding from openssl encryption call...

Synovitis answered 16/12, 2016 at 10:9 Comment(11)
You should never get a perfect match, EVER. That's what initialization vector is for. Every time you encrypt the same payload with the same algorithm and the same key, you should get completely different output if you want to be safe. If you get the same output for same input, your encryption is weak. That's the point of IV in encryption. Once encrypted, you deliver encrypted payload with IV to the other side.Kiloton
If I'm using the same payload, same algo, same IV; I reckon I should be getting the same output. Never mind that that I should use a different IV for each call: using the same IV for both calls (mcrypt and openssl), I think I should get the same output, right? I'm starting to believe is related to message padding, since the begging of the output is the same.Synovitis
If everything is the same then you should be getting the same output, correct. 4th parameter for openssl_encrypt controls the padding. You can encrypt with $encrypted = openssl_encrypt($data, $alg, $key, OPENSSL_ZERO_PADDING, $iv); and check if you get the same output. At www.php.net/openssl_encrypt, you can read the comments to see how to use 4th parameter.Kiloton
OPENSSL_ZERO_PADDING doesn't work, at least for me ... but yes - padding is why you can't get the same result. However, there's so many thinks wrong with your encryption scheme, that you might as well just replace it with a new one - please use a library for that, don't roll your own.Romany
Right now is about to understand how to make this call and get the same results than with mcrypt.Thanks for your comments on encryption. This is neither the full solution, nor I have the authority to change everything I'd want in this codebase.Synovitis
Well, that's the problem - properly padding the data yourself is key to getting the same results, and if you do that you are changing the encryption scheme. I know, you're trying to avoid BC breaks, but that's not possible here, so you should do yourself a favor and do the entire thing properly.Romany
I guess is not solvable then. I need to create this message to compare with a verification messaged provided by a third party (for which I have a $key and $iv provided separately). I have no control over this third party encryption methods, I can only check against the encrypted message they provide, which apparently matches mcrypt response, but not openssl's. If OPENSSL_ZERO_PADDING worked, all would be dandy.Synovitis
Please read this q&a, some things changed regarding the padding in the IV and you should in any case use mcrypt_create_iv().Errol
It is best not to use mcrypt, it has been abandonware for nearly a decade now. It has therefore been deprecated and will be removed from the core and into PECL in PHP 7.2. It does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data. mcrypt has many outstanding bugs dating back to 2003. Instead consider using defuse or RNCryptor, they provide a complete solution, are being maintained and is correct.Tigrinya
This question is about not using mcrypt.Synovitis
Also see Upgrading my encryption library from Mcrypt to OpenSSL and Preparing for removal of Mcrypt in PHP 7.2Enright
P
37

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. Apparently OpenSSL doesn't allow you to use zero padding in this case, which explains the false return value.

You can circumvent this by adding the padding manually.

$message = "Lorem ipsum";
$key = "123456789012345678901234";
$iv = "12345678";

$message_padded = $message;
if (strlen($message_padded) % 8) {
    $message_padded = str_pad($message_padded,
        strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");
}
$encrypted_mcrypt = mcrypt_encrypt(MCRYPT_3DES, $key,
    $message, MCRYPT_MODE_CBC, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "DES-EDE3-CBC", 
    $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);

printf("%s => %s\n", bin2hex($message), bin2hex($encrypted_mcrypt));
printf("%s => %s\n", bin2hex($message_padded), bin2hex($encrypted_openssl));

This prints both as equal.

4c6f72656d20697073756d => c6fed0af15d494e485af3597ad628cec
4c6f72656d20697073756d0000000000 => c6fed0af15d494e485af3597ad628cec
Phasia answered 16/1, 2017 at 13:0 Comment(0)
M
-3

mcrypt_encrypt uses zeroes to pad message to the block size. So you can add zeroes to the tail of your raw data, and then encrypt the block.

Using OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING should work. If it doesn't, then you can remove padding from the decrypted data by yourself.

Martel answered 16/1, 2017 at 9:43 Comment(4)
Why should I spend time on that?Martel
OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING do not work. Probably a bug in phps openssl implementation or something? Removing the extra characters from the unencrypted data doesn't work for me. This string is used as a verification signature: I'm provided with one and a some data, create this encrypted data to match the received signature. I don't control the system on the other side. Modifying mcrypt_ call so it matches openssl_ doesn't help me, I need openssl_ to match mcrypt_ :)Synovitis
You can add zero padding by yourself, then call openssl_encrypt. This will add openssl's padding as well. Then remove that padding (last 8 bytes). Then output should match mcrypt_encryptMartel
I'm not sure I'm following. Can you post this as part of your answer, with a code example showing what you mean? Thanks.Synovitis

© 2022 - 2024 — McMap. All rights reserved.