0

I have a PHP application using openSSL to send a AES-256-CBC encrypted string with Base64 encoding to a .NET application. I start the decrypt method, but I always error when I try to actually decrypt the data. The error states "Padding is invalid and cannot be removed". What am I doing wrong here?

The encrypted Base64 value generated by the PHP function is: p07cNwcvcYLxvYHCUsmZqKYr40IXXYjEHr7r+JdgXiJT5/wpDSDmr48JLOXyNEL7

The key is: M2AZULUALPHA

The salt is: TripBuilder2017x

The PHP function is:

function encrypt($text) {
$key = "M2AZULUALPHA";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);

$crypttext = openssl_encrypt($text,'aes-256-cbc', $key,  OPENSSL_RAW_DATA, 'TripBuilder2017x');
return base64_encode($crypttext);

}

The .NET Decrypt function:

 private string Decrypt(string cipherText)
        {
            string EncryptionKey = "M2AZULUALPHA";
            byte[] saltArray = Encoding.ASCII.GetBytes("TripBuilder2017x");


            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, saltArray);
                encryptor.Mode = CipherMode.CBC;
                encryptor.BlockSize = 128;
                encryptor.KeySize = 256;
                encryptor.Padding = PaddingMode.PKCS7;
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }


            }
            return cipherText;
        }

UPDATE I went to an online openSSL decrypt tool, entered my IV,Key and base64 text. It generated the correct string but had some weird blocks in the string.

UPDATE 2 Here is the updated PHP code. I increased the key size to 16 bytes as well. I am still receiving the same error.

$key = "M2AZULUALPHAECHO";
$salt = "TripBuilder2017x";
$mode = "aes-256-cbc";
$text = "BrassMonkey";

function encrypt($text,$key,$salt,$mode) {

    return base64_encode(encryptplain($text,$key,$salt,$mode));
}

function encryptplain($text,$key,$salt,$mode) {


    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $padding = $block - (strlen($text) % $block);
    $text .= str_repeat(chr($padding), $padding);

    $crypttext = openssl_encrypt($text, $mode, $key, 1, $salt);
    return ($crypttext);
}

UPDATE 3: My .NET Code

private string Decrypt(string cipherText)
        {
            string EncryptionKey = "M2AZULUALPHAECHO";
            byte[] saltArray = Encoding.ASCII.GetBytes("TripBuilder2017x");

            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {

                encryptor.Mode = CipherMode.CBC;
                encryptor.BlockSize = 128;
                encryptor.KeySize = 256;
                encryptor.Padding = PaddingMode.PKCS7;
                encryptor.Key = Encoding.ASCII.GetBytes(EncryptionKey);
                encryptor.IV = saltArray;

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }


            }
            return cipherText;
        }
Chris Lombardi
  • 861
  • 2
  • 14
  • 31
  • I had a similar problem, and my conclusion was openssl won't return any binary data :(, I used mcrypt instead – Cr1xus Mar 31 '17 at 14:07
  • I had the same issue with MCrypt which is why I tried OpenSSL. Since I am getting the same error I have a feeling it has more to do with the properties I am assigning values to. – Chris Lombardi Mar 31 '17 at 14:23
  • I would not recommend the use of mcrypt as this is a deprecated function in the current PHP release. The blocks in the online viewer is probably the cause of characters which are not available in the charset used by your browser/site. – AltShiftZero Mar 31 '17 at 15:54
  • mcrypt_get_block_size returns the blocksize in bytes while Aes blocksize expects bits. This could be your mismatch I guess. – AltShiftZero Mar 31 '17 at 16:07
  • @DempseyLawrence Do we even have to get the block size and is there an equivalent method in OpenSSL? – Chris Lombardi Mar 31 '17 at 16:27
  • @ChrisLombardi We don't. Openssl_encrypt does this automaticly as far as I know. – AltShiftZero Mar 31 '17 at 20:11
  • Do you need it to be that secure? try openssl without the salt; look I posed a what-so similar question http://stackoverflow.com/questions/42790080/unable-to-download-binary-file-in-php – Cr1xus Apr 01 '17 at 09:09

1 Answers1

3

I can see several immediate problems:

  1. Your C# decryption code is using PBKDF2 to derive the AES key and IV from a password and salt, while your PHP encryption code is using the password and salt directly as the AES key and IV. You need to use the same logic in both places if you want it to work properly.
  2. You're specifying the wrong encryption algorithm in your PHP code when calculating the block length and padding your input - the various versions of Rijndael are named according to their block length, while all versions of AES have a 128-bit block length (i.e. Rijndael-128) and use varying key lengths. Thus, you want to specify MCRYPT_RIJNDAEL_128 if you want the proper padding.
  3. You shouldn't perform padding yourself during encryption unless you also specify the OPENSSL_ZERO_PADDING flag - if you fail to specify this flag, OpenSSL will add its own PKCS#7 padding to the input text, extending it by another block, and your decryption logic will only strip off that part, ignoring your manually-added padding (and resulting in the "weird blocks in the string" you observed with your online OpenSSL decrypt tool).
  4. You need to make sure that your specified AES key size actually matches the algorithm you specified - PHP's openssl_encrypt() function extends your key size (with null bytes) to match the algorithm you specified, while C# will automatically adjust the algorithm to match your provided key size. In this case, your C# code is actually performing AES-128 decryption, which is incompatible with the AES-256 encryption being done in your PHP code.

Other remarks:

  1. If you want a Base64-encoded result from openssl_encrypt(), you can omit the OPENSSL_RAW_DATA flag and it'll return Base64-encoded ciphertext.
  2. The proper solution to problem #1 should be to perform key derivation in both places, or to use binary keys and IV values - a plaintext key has far less entropy and is significantly less secure.
  3. You shouldn't be hardcoding the key and IV in your code - the key should be provided as a parameter, and the IV should be randomly generated and subsequently associated with the ciphertext.
Quietust
  • 257
  • 1
  • 7
  • Thank you for the suggestions. I changed the key value to use Encoding.ASCII.GetBytes(EncryptionKey); and the salt array to use the byte array I created previously. encryptor.IV = saltArray;.. When I ran this code just as a test without making the other changes it said that my Key was not long enough so I added more characters to make it 16bytes and then I was able to proceed and was prompted with the same padding error. I am going to implement the PHP changes now. The key was also updated and my original tests had the "x". I am not sure how I lost that in my copy and paste. – Chris Lombardi Mar 31 '17 at 14:32
  • Thank you. I made the changes you suggested not including the ones listed under remarks and I am getting the same error. See the above PHP code update. – Chris Lombardi Mar 31 '17 at 15:35
  • I updated my C# code. What is making my decrypt method an AES128 Decrypt exactly? Sorry, this is my first time messing around with encryption in code. – Chris Lombardi Mar 31 '17 at 17:07
  • The value of encryptor.KeySize appears to be ignored if you specify encryptor.Key manually (if you leave Key *null*, it will call GenerateKey() instead). Since your key is 16 bytes long (i.e. 128 bits), it does AES-128. – Quietust Mar 31 '17 at 17:14
  • So for testing purposes if I make the key double the length it should work? I know I am not using best practices for some of this, but I am just trying to learn more about my mistake, get it working and work backwards from there. – Chris Lombardi Mar 31 '17 at 17:22
  • Either double your key length (in *both* places), or change your PHP code to use "aes-128-cbc" with your existing key. Also take note of my comment about doing padding during encryption. – Quietust Mar 31 '17 at 17:52