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):
- Given the size of the modulus you padd k1 bits with 0 and k0 bits with a random number.
- 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:
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 Rfc2898DeriveBytes
would 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
How to Disable Cascade Delete for Link Tables in Ef Code-First
Save Modified Wordprocessingdocument to New File
Is There an Exponent Operator in C#
Fill Panel with Gradient in Three Colors
How to Concatenate Two System.Io.Stream Instances into One
Does Reactive Extensions Support Rolling Buffers
Breakpoint Failed to Bind - Visual Studio 2015
Avoiding Null Reference Exceptions
Wpf Combobox: Different Template in Textbox and Drop-Downlist
Trace Listener to Write to a Text Box (Wpf Application)
How to Remove a Single Attribute (E.G. Readonly) from a File
Does Dispose Still Get Called When Exception Is Thrown Inside of a Using Statement
How to Convert String to Integer in C#
Can Razor Class Library Pack Static Files (Js, CSS etc) Too
How to Create a Dictionary of Generic Types
.Net Application Cannot Start and Receive Xamlparseexception