Why Does a Bad Password Cause "Padding Is Invalid and Cannot Be Removed"

Why does a bad password cause Padding is invalid and cannot be removed?

Although this have been already answered I think it would be a good idea to explain why it is to be expected.

A padding scheme is usually applied because most cryptographic filters are not semantically secure and to prevent some forms of cryptoatacks. For example, usually in RSA the OAEP padding scheme is used which prevents some sorts of attacks (such as a chosen plaintext attack or blinding).

A padding scheme appends some (usually) random garbage to the message m before the message is sent. In the OAEP method, for example, two Oracles are used (this is a simplistic explanation):

  1. Given the size of the modulus you padd k1 bits with 0 and k0 bits with a random number.
  2. Then by applying some transformation to the message you obtain the padded message wich is encrypted and sent.

That provides you with a randomization for the messages and with a way to test if the message is garbage or not. As the padding scheme is reversible, when you decrypt the message whereas you can't say anything about the integrity of the message itself you can, in fact, make some assertion about the padding and thus you can know if the message has been correctly decrypted or you're doing something wrong (i.e someone has tampered with the message or you're using the wrong key)

Padding is invalid and cannot be removed?

Rijndael/AES is a block cypher. It encrypts data in 128 bit (16 character) blocks. Cryptographic padding is used to make sure that the last block of the message is always the correct size.

Your decryption method is expecting whatever its default padding is, and is not finding it. As @NetSquirrel says, you need to explicitly set the padding for both encryption and decryption. Unless you have a reason to do otherwise, use PKCS#7 padding.

padding is invalid and cannot be removed decrypt value

You are hashing the password when you decrypt,

passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

but not when encrypt. This means you are using different passwords

Padding is invalid and cannot be removed using AesManaged

The trick is to use MemoryStream.ToArray().
I also changed your code so that it uses the CryptoStream to Write, in both encrypting and decrypting. And you don't need to call CryptoStream.FlushFinalBlock() explicitly, because you have it in a using() statement, and that flush will happen on Dispose(). The following works for me.

byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");

using (Aes aes = new AesManaged())
{
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 128; // in bits
aes.Key = new byte[128/8]; // 16 bytes for 128 bit encryption
aes.IV = new byte[128/8]; // AES needs a 16-byte IV
// Should set Key and IV here. Good approach: derive them from
// a password via Cryptography.Rfc2898DeriveBytes
byte[] cipherText= null;
byte[] plainText= null;

using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(rawPlaintext, 0, rawPlaintext.Length);
}

cipherText= ms.ToArray();
}

using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
}

plainText = ms.ToArray();
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);
}

Also, I guess you know you will want to explicitly set the Mode of the AesManaged instance, and use System.Security.Cryptography.Rfc2898DeriveBytes to derive the Key and IV from a password and salt.

see also:

- AesManaged

Padding is invalid and cannot be removed -Whats wrong with this code?

You need to set the BlockSize and the KeySize before you set the Key and the IV. Additionally you should probably be generating a random IV for each message and note that ICryptoTransform implements IDisposable so these objects should be disposed.

Padding is invalid and cannot be removed - decryption in c# (encryption done in java)

I used my own decryption method for c# aes and hashKey as well as I know they are working :-).

Giving these data as input for the Java encryption:

String string_key = "mySecretKey";
String stringToEncrypt = "The quick brown fox jumps over the lazy dog";

I'm getting the

encryptedLicense: lrgzfdMTetZKeAFlCAbCDBL4VhtpVGdhTESl9QNgs5b0KOWke1CnKQQf+xmB+/mK

Using this string and the given string_key to C# is puts out:

plaintext expect : The quick brown fox jumps over the lazy dog
plaintext from C#: The quick brown fox jumps over the lazy dog

Security warning: this code uses a fixed encryption key and a fixed initialization vector that makes the
complete encryption UNSECURE. The code has not proper exception handling and is for educational purpose only.

C#-code:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class Program {
public static void Main() {
Console.WriteLine("Padding is invalid and cannot be removed - decryption in c# (encryption done in java)");

string plaintextExpected = "The quick brown fox jumps over the lazy dog";
string encryptedLicense = "lrgzfdMTetZKeAFlCAbCDBL4VhtpVGdhTESl9QNgs5b0KOWke1CnKQQf+xmB+/mK";
string string_key = "mySecretKey";
string plaintext = "";

// decryption
plaintext = decryptFromBase64(hashKey(string_key), encryptedLicense);
Console.WriteLine("plaintext expect : " + plaintextExpected);
Console.WriteLine("plaintext from C#: " + plaintext);
}

public static byte[] hashKey(string value)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value);
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] sha1Hash = sha.ComputeHash(bytes);
byte[] sha1HashLength = new byte[16];
Array.Copy(sha1Hash, 0, sha1HashLength, 0, 16);
return sha1HashLength;
}

static string decryptFromBase64(byte[] key, string data) {
string decryptedtext;
using(Aes aesAlg = Aes.Create()) {
aesAlg.Key = key;
string initVector = "RgUkXp2s5v8y/B?E";
byte[] IV = Encoding.UTF8.GetBytes(initVector);
byte[] cipherText = Convert.FromBase64String(data);
aesAlg.IV = IV;
aesAlg.Mode = CipherMode.CBC;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using(var msDecrypt = new MemoryStream(cipherText)) {
using(var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) {
using(var srDecrypt = new StreamReader(csDecrypt)) {
decryptedtext = srDecrypt.ReadToEnd();
}
}
}
}
return decryptedtext;
}
}

Edit 1:
In my Java code I changed the following line:

//String string_key = "mySecretKey";
String string_key = "BFEBFBFF000306D4";

and got as result:

encryptedLicense: 7QWzpg/rVE6AhmcGphFT9uswU8hgKjG3i9NHJHCn/fKITYNIljOb28+cbh5y5JEA

Changing my C#-code as follow:

//string encryptedLicense = "lrgzfdMTetZKeAFlCAbCDBL4VhtpVGdhTESl9QNgs5b0KOWke1CnKQQf+xmB+/mK";
string encryptedLicense = "7QWzpg/rVE6AhmcGphFT9uswU8hgKjG3i9NHJHCn/fKITYNIljOb28+cbh5y5JEA";
//string string_key = "mySecretKey";
string string_key = "BFEBFBFF000306D4";

gets the result:

plaintext expect : The quick brown fox jumps over the lazy dog
plaintext from C#: The quick brown fox jumps over the lazy dog

So everything is working.

ASP.NET AES Padding is invalid and cannot be removed

The immediate problem is that your test code is misusing Rfc2898DeriveBytes/ PBKDF. It has nothing to do with the database. Each time you call GetBytes() on a given instance, it returns a different set of bytes (by design!). This is apparent with a bit of code like this:

    Dim salt = CryptoTools.GetRandomBytes(16)
Dim PBKDF = New Rfc2898DeriveBytes("secret", salt, 10000)

Dim a = PBKDF.GetBytes(32)
Dim b = PBKDF.GetBytes(32)
Dim c = PBKDF.GetBytes(32)

If a.SequenceEqual(b) = False Then
Console.WriteLine("A != B")
End If
...

It's very clear with Intellisense:

Sample Image

The arrays are not at all alike. From the Remarks section on MSDN for GetBytes:

The Rfc2898DeriveBytes class takes a password, a salt, and an iteration count, and then generates keys through calls to the GetBytes method. Repeated calls to this method will not generate the same key; ...

The emphasis is mine, but is intended to point out that it is a key generator not just a hasher. So, in your "Fails with Database" code you have this block:

Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
...
Using myAes As Aes = Aes.Create()
...
myAes.Key = key.GetBytes(myAes.KeySize / 8)
myAes.IV = key.GetBytes(myAes.BlockSize / 8)
...
End Using

The Key and IV generated will not be the same. This is good, but probably not what you intended. The same block is used later to decrypt what was read back from the db. The Key and IV used to decrypt will also be different from each other, but more importantly they will not be the same Key and IV used to encrypt!

This is a fatal flaw, and causes the error. It seems like a misleading error message, but it is simple to reproduce/fix. You need a new PBKDF to 'start over' in the decryption part, or Reset the existing one:

' after this line...
Dim decrypted As String = ""
' ...add this:
key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
' or
'key.Reset()

It is more likely that the real code would not be doing a roundtrip in the same method, and using a new Rfc2898DeriveByteswould be created.

Finally, note that the IV and Salt should be unique and random for effective encryption. There really isn't much point in encrypting each row with the same password, key, salt and IV beyond the illusion of security. Someone only has to crack the PW for one row to get the data for all the rows.

Because Rfc2898DeriveBytes is deterministic, it can play a role in generating the IV and Salt (for the data to be encrypted) without having to save them or use static ones.



Related Topics



Leave a reply



Submit