phpopensslmcrypt

How to convert mcrypt_encrypt into openssl_encrypt?


I wanted to improve a certain wordpress plugin that uses the now obsolete "mcrypt". Instead of it, I would like to use the OpenSSL library to encrypt the submited data, but during the encryption I ran into problems, namely: the openssl_encrypt function returns a different value than mcrypt_encrypt by which the system I am connecting to does not return me the correct data, and its owner has no way to send me logs of what I uploaded to it :(

I've already scoured the internet the length and breadth of the Internet, but have not found a solution. I suspect that the problem is padding, but I can't find a solution. Can you help?

Below are the insides of my PHP object $password, $salt and $iv are obviously changed

class EncryptDebug{
private $algo = 'sha1';
private $password = 'ab4232goodcf423484422c90c3e4aa7c';
private $salt = 'ascastas54490a31';
private $iv = '8947da32awl55kwj'
private $lenght = 16;
private function generate_key(){
    return hash_pbkdf2( $this->algo , $this->password , $this->salt, 100, $this->lenght, true );
}
public function encryptSSL($plaintext){
    $key = $this->generate_key();
    $ciphertext = base64_encode(openssl_encrypt($plaintext, 'AES-128-CBC', $key,  OPENSSL_ZERO_PADDING, $this->iv));
    
    return str_replace('+', '%2B', $ciphertext);

}
public function encryptMCRYPT($plaintext){
    $key = $this->generate_key();
    $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $this->iv));
    
    return str_replace('+', '%2B', $ciphertext);

}
}

Forgot to mention: OPENSSL_ZERO_PADDING returns error. Using OPENSSL_RAW_DATA I'm able to get results similar to mcrypt_encrypt, but the ending is different, example:

OpenSSL: rPzVvF7gaPMA4ADAjHUW8Wy1ThTJG%2BVPdcz5iKAkAwrDTTFTcOpWgWOCh9l9JFZ8WcNzMJ868026TkUxcYJMrQ==

MCRYPT: rPzVvF7gaPMA4ADAjHUW8Wy1ThTJG%2BVPdcz5iKAkAwrDTTFTcOpWgWOCh9l9JFZ8UGVfF091Q9bY61mTRg%2BBSg==


Solution

  • In encryptSSL() it is currently Base64 encoded twice, once explicitly, once implicitly by default. One of the Base64 encodings must therefore be removed, either the explicit or the implicit one. The former is achieved by removing the base64_encode() call, the latter by setting the OPENSSL_RAW_DATA flag.

    In addition, mcrypt uses Zero padding and PHP/OpenSSL uses PKCS#7 padding. Therefore, so that encryptSSL() gives the same result as encryptMCRYPT(), Zero padding must be used. Since PHP/OpenSSL does not support Zero padding, PKCS#7 padding must be disabled (with the OPENSSL_ZERO_PADDING flag) and Zero padding must be implemented explicitly.

    Overall:

    $ciphertext = openssl_encrypt($this->zeroPad($plaintext, 16), 'AES-128-CBC', $key,  OPENSSL_ZERO_PADDING, $this->iv); // remove base64_encode(), zero pad plaintext, disable PKCS#7 padding 
    

    with:

    protected function zeroPad($text, $bs) {
        $pad = $bs - strlen($text) % $bs;
        return ($pad < 16) ? $text .  str_repeat("\0", $pad) : $text;
    }
    

    With these changes, both functions give the same result.

    Be aware, that Zero padding is unreliable compared to PKCS#7 padding.


    Security:

    Note that a static IV and a static salt are vulnerabilities. Instead, both are to be randomly generated and passed to the decrypting side along with the ciphertext, typically concatenated (both are not secret).

    Furthermore, an iteration count of 100 for PBKDF2 is usually far too small.