Decrypt mcrypt with openssl

2019-01-27 03:04发布

Since mcrypt is considered obsolete, my task is upgrading the current code to use openssl. Sounds simple, but ... after a few days of try and failure I feel like going insane.

My question to you is: Is there any way you can decrypt with openssl data previously encrypted with mcrypt? I've read so many posts on this matter and most of them say that a previous manual padding of the data was/is necessary before running mcrypt on it. The issue is that the mcrypt-ed data is already encrypted (with the automatic null padding mcrypt provides) and resides in a database, so modification of that is not possible and/or desired.

Mentions:

  1. the algorithm used is rijndael-128 cbc with a 32-byte key (so I'm using aes-256-cbc for openssl).
  2. I'm using an openssl wrapper for php (php-crypto).
  3. I've managed to make the inverse operation work (decode openssl with mcrypt) by simply stripping the end decoded characters if they were non alpha-numerical.
  4. Manually padding the data before mcrypt-ing and then decrypting it using openssl works like a charm, but that's not the problem here.

Some code snippets:

// Simple mcrypt encrypt, decrypt with php-crypto example
// This doesn't work and produces a "Finalizing of cipher failed" error
        $data = "This is a text";
        $strMcryptData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);

        $algorithm = 'aes-256-cbc';
        $cipher = new Cipher($algorithm);
        $sim_text = $cipher->decrypt($strMcryptData, $key, $iv);

// Simple mcrypt encrypt with padding, decrypt with php-crypto
// Works and produces the correct text on decryption
        $pad =  $blocksize - (strlen($data) % $blocksize);
        $text = $data;
        $text .= str_repeat(chr($pad), $pad);
        $strPaddedData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);

        $sim_text = $cipher->decrypt($strPaddedData, $key, $iv);

3条回答
劳资没心,怎么记你
2楼-- · 2019-01-27 03:36

Slightly old, but you can solve this with a bit of work. You can tell PHP's OpenSSL that the encrypted string is not padded, and tell it to give you the raw output (So you don't have to base64 decode it, either). You can then strip nulls from the end of the resulting string if the length of the string happens to be perfectly divisible by the IV (This is a sanity check, as if the resulting string isn't divisible by the IV then it wasn't padded at all).

Be aware, this code has two major limitations:

  1. If, at any point, you encrypted a legitimate string that ended in two or more NULL bytes then this code will not give you the same output.

  2. If the padding of the string needed only one null byte, then this code won't strip it.

You can solve both of these if you know for a FACT that you didn't encrypt anything that ends in null bytes, you can alter the code that strips the nulls to just do a preg_replace; just make sure you anchor the regex to the end of the string so it only strips from the end.

http://3v4l.org/kYAXn

Obviously this code comes with no major disclaimer and please test it in your use case, but someone might hopefully find this useful.

查看更多
Ridiculous、
3楼-- · 2019-01-27 03:49

If you encrypt in mcrypt without adding PKCS7 manually, mcrypt will happily pad your plaintext with NUL bytes.

OpenSSL will do PKCS7 padding for you whenever using aes-X-cbc. The unfortunate consequence of this is that if you have AES-CBC(NULL_PADDED(plaintext)) and try to decrypt it, openssl_decrypt will attempt to remove the padding and fail.

Compare http://3v4l.org/bdQe9 vs http://3v4l.org/jr68f and http://3v4l.org/K6ZEU

The OpenSSL extension does not currently offer you a way to say "This string is not padded, please don't strip the padding for me" and then remove the NUL bytes on your own. You must encrypt with PKCS7 padding in order for decryption to succeed.

Although this is a limitation of OpenSSL, it bears emphasizing that the only reason you're running into it is because mcrypt is terrible.

查看更多
Fickle 薄情
4楼-- · 2019-01-27 03:52

There shouldn't be any major differences except for the padding. You should be able to call EVP_CIPHER_CTX_set_padding if you use the higher level OpenSSL (EVP) constructs directly. I presume that the padding argument should be zero, although it is not documented. You need a preconfigured encryption/decryption context for this.

Afterwards you will have your plaintext of the same length as the ciphertext. Zero to fifteen bytes at the end will be set to zero. You need to remove these bytes manually. If the plaintext happens to end with zero bytes then those will also be removed; that's however never the case if the plaintext is a printable string (that uses 8 bit encoding). You may want to ensure that you don't remove more than 15 bytes.

If you get completely random plaintext then your key or ciphertext is incorrect. If you get readable plaintext but for the first 16 bytes then your IV handling is incorrect.

查看更多
登录 后发表回答