Flo
Flo

Reputation: 331

Specified padding mode is not valid for this algorithm when using AES & PKCS#7 padding in .Net core 2.0

I spent a whole day investigating this and search all related questions on Stack Overflow for this question so please don't mention about possible duplicates.

The code below gives me a System.Security.Cryptography.CryptographicException: 'Specified padding mode is not valid for this algorithm.'

While using the very same parameters on this website : http://aes.online-domain-tools.com it decrypts perfectly into "Hello world" then filled with five 'x05' bytes for padding (PKCS#7 padding).

However the code below will always yield an exception when calling the TransformFinalBlock()

Context: Console application running on Win8.1 with .NET Core 2.0 / Algorithm is AES / CBC / padding PKCS#7

I also tried the proposed solution here: Specified padding mode is not valid for this algorithm - c# - System.Security.Cryptography but no success (I also don't understand why if IV is already set in the SymmetricAlgorithm instance, it should be used later on when deciphering?

    static void Main(string[] args)
    {
        string encryptedStr = "e469acd421dd71ade4937736c06fdc9d";
        string passphraseStr = "1e089e3c5323ad80a90767bdd5907297b4138163f027097fd3bdbeab528d2d68";
        string ivStr = "07dfd3f0b90e25e83fd05ba338d0be68";

        // Convert hex strings to their ASCII representation
        ivStr = HexStringToString(ivStr);
        passphraseStr = HexStringToString(passphraseStr);
        encryptedStr = HexStringToString(encryptedStr);

        // Convert our ASCII strings to byte arrays
        byte[] encryptedBytes = Encoding.ASCII.GetBytes(encryptedStr);
        byte[] key = Encoding.ASCII.GetBytes(passphraseStr);
        byte[] iv = Encoding.ASCII.GetBytes(ivStr);

        // Configure our AES decryptor
        SymmetricAlgorithm algorithm = Aes.Create();
        algorithm.Mode = CipherMode.CBC;
        algorithm.Padding = PaddingMode.PKCS7;
        algorithm.KeySize = 256;
        //algorithm.BlockSize = 128;
        algorithm.Key = key;
        algorithm.IV = iv;

        Console.WriteLine("IV length " + iv.Length);            // 16
        Console.WriteLine("Key length " + key.Length);          // 32

        ICryptoTransform transform = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV);

        // Perform decryption
        byte[] outputBuffer = transform.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

        // Convert it back to a string
        string result = Encoding.ASCII.GetString(outputBuffer);

        Console.WriteLine(result);
        Console.ReadLine();
    }

    public static string HexStringToString(string hexString)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < hexString.Length; i += 2)
        {
            var hexChar = hexString.Substring(i, 2);
            sb.Append((char)Convert.ToByte(hexChar, 16));
        }

        return sb.ToString();
    }

Upvotes: 4

Views: 3997

Answers (1)

CodeFuller
CodeFuller

Reputation: 31312

The problem is in the way how you convert hex string to byte array. Try to debug your code and check the value of array encryptedBytes. You'll see the following array:

{ 0x3f, 0x69, 0x3f, 0x3f, 0x21, 0x3f, 0x71, 0x3f, 0x3f, 0x3f, 0x77, 0x36, 0x3f, 0x6f, 0x3f, 0x3f }

which is far from input e469acd421dd71ade4937736c06fdc9d.

You shouldn't use System.String object as just a holder of binary char codes because .Net strings are UTF16-encoded.

Now when root cause is clear, the fix is pretty straighforward. Change your HexStringToString method so that it converts hex string to bytes array directly:

public static byte[] HexStringToByteArray(string hexString)
{
    if (hexString.Length % 2 != 0)
    {
        throw new InvalidOperationException($"Inalid hex string '{hexString}'");
    }

    byte[] bytes = new byte[hexString.Length / 2];
    for (var i = 0; i < hexString.Length; i += 2)
    {
        var hexChar = hexString.Substring(i, 2);
        bytes[i / 2] = Convert.ToByte(hexChar, 16);
    }

    return bytes;
}

Then adjust the code in Main():

byte[] encryptedBytes = HexStringToByteArray(encryptedStr);
byte[] key = HexStringToByteArray(passphraseStr);
byte[] iv = HexStringToByteArray(ivStr);

This will give you desired Hello world in result variable.

Upvotes: 3

Related Questions