Search code examples
c#exceptionaespaddingspace

C# AES encryption: Starting/Ending space throw CryptographicException: 'The input data is not a complete block


I'm studying AES with C# and wrote some codes to encrypt string. At first, it throws exception below.

System.Security.Cryptography.CryptographicException: 'Padding is invalid and cannot be removed.'

I solved this by setting padding mode for encryption/decryption as 'None'.

And then, I put some spaces before/after the string to be encrypted. Then, I get below exception.

System.Security.Cryptography.CryptographicException: 'The input data is not a complete block.'

So, I removed padding setting for encryption. And then, everything looks okay.

My questions are

  1. Why do I have to specify the padding mode?
  2. After the string with starting/ending spaces is converted into byte array, what I can see is just a byte array. Bunch of numbers. Do encryption/decryption care about kind of byte coming to before/after the string?
  3. Why does 'setting padding mode none only for decryption' work? I expected symmetric padding setting for both encryption/decryption. For the AES, the default padding for encryption/decryption 'PKCS7'.

Thanks in advance.

public void ByteEncryption()
{
    byte[] key = new byte[16];
    byte[] iv = new byte[16];

    for (int ii = 0; ii < 16; ii++)
    {
        key[ii] = (byte)ii;
        iv[ii] = (byte)ii;
    }

    string str_data = "   This is a string to be encrypted    ";
    byte[] data = Encoding.Unicode.GetBytes(str_data);
    byte[] encrypted = EncryptMemory(data, key, iv);
    foreach(byte b in encrypted)
        Console.WriteLine(b);

    string str_encrypted = Encoding.Unicode.GetString(encrypted);
    Console.Write("Encrypted:");
    Console.WriteLine(str_encrypted);

    byte[] decrypted = DecryptMemory(encrypted, key, iv);
    string str_decrypted = Encoding.Unicode.GetString(decrypted);
    Console.Write("---");
    Console.Write(str_decrypted);
    Console.WriteLine("---");
}

byte[] EncryptMemory(byte[] data, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algorithm = Aes.Create())
    {
        algorithm.Padding = PaddingMode.None; // Throw exception                
        using (ICryptoTransform encryptor = algorithm.CreateEncryptor(key, iv))
        {
                return Crypt(data, encryptor);
        }
    }
}

byte[] DecryptMemory(byte[] data, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algorithm = Aes.Create())
    {
        algorithm.Padding = PaddingMode.None;
        using (ICryptoTransform decryptor = algorithm.CreateDecryptor(key, iv))
        {
            return Crypt(data, decryptor);
        }
    }
}

byte[] Crypt(byte[] data, ICryptoTransform cryptor)
{
    using (MemoryStream memory = new MemoryStream())
    using (CryptoStream stream = new CryptoStream(memory, cryptor, CryptoStreamM    ode.Write))
    {
        stream.Write(data, 0, data.Length);
        return memory.ToArray();
    }
}

Solution

  • From this link, after reading cadrell0's comment below,

    Good call on Dispose. I was calling ms.ToArray() before disposing CryptoStream. Moving that line outside of the using fixed it for me

    I solved the problem. This had been torturing me for 2 days. Answering my questions, all the misunderstanding came from the length of the string I was encrypting. By inserting random number of characters, the length of input string without special character was exactly multiple of 16 accidently; Size of unicode is 16 bit. The original string without special character didn't need a padding as it was already multiple of 16.