0
votes

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;
        }
1
I had a similar problem, and my conclusion was openssl won't return any binary data :(, I used mcrypt insteadCr1xus
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
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
mcrypt_get_block_size returns the blocksize in bytes while Aes blocksize expects bits. This could be your mismatch I guess.AltShiftZero
@DempseyLawrence Do we even have to get the block size and is there an equivalent method in OpenSSL?Chris Lombardi

1 Answers

2
votes

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.