Reputation: 41
I'm trying to share data between a Xamarin/.Net/C# prgram and a python program using AES CBC. I'm able to encrypt a message in .Net and and decrypt that message in python successfully, but not vice versa. That is, when I encrypt in python first and try to decrypt that message in C#, I get the exception: "Bad PKCS7 padding. Invalid length 0"
Here is my python encryption using in Python:
salt = 16 * b'\0'
keyIV = PBKDF2(Config.SECRET, salt).read(48)
key = keyIV[:32]
iv = keyIV[-16:]
aes = AES.new(key, AES.MODE_CBC, iv)
# padding
length = 16 - (len(textToEncrypt) % 16)
print(len(textToEncrypt))
textToEncrypt += length * b'\0'
print(len(textToEncrypt))
encrypted = aes.encrypt(textToEncrypt)
encoded = base64.b64encode(encrypted)
return encoded
And here is the decryption in C#:
if (textToDecrypt == null || textToDecrypt.Length == 0)
return "";
textToDecrypt = textToDecrypt.Replace(" ", "+");
byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
string decryptedText;
using (Aes aes = Aes.Create())
{
byte[] salt = new byte[16];
Rfc2898DeriveBytes crypto = new Rfc2898DeriveBytes(Config.SECRET, salt);
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = crypto.GetBytes(32);
aes.IV = crypto.GetBytes(16);
using (MemoryStream mStream = new MemoryStream())
{
using (CryptoStream cStream = new CryptoStream(mStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cStream.Write(bytesToDecrypt, 0, bytesToDecrypt.Length);
}
decryptedText = Encoding.Unicode.GetString(mStream.ToArray());
}
}
return decryptedText;
The key and iv match between programs, but the encryption string in python comes out to be much shorter than encrypting the same string in C#. Thanks for any help.
Upvotes: 1
Views: 1577
Reputation: 42225
length = 16 - (len(textToEncrypt) % 16) textToEncrypt += length * b'\0'
That's not how PKCS7 padding works.
The thing about padding is that you need a way to identify it so that you can remove it later, without accidentally leaving some behind or taking off some data thinking that it was padding.
PKCS7 does this by padding with bytes whose value is the number of padding bytes. So the padding will be one of
01
02 02
03 03 03
04 04 04 04
...
16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16
Why have a padding of 16? Well if the length of your data is an exact multiple of the block size (16 bytes), and you opted not to add any padding at all, then when you came to remove the padding you might find that your data naturally ended in 01
or 02 02
, etc, and you'd remove those valid data bytes thinking that they were padding. So you need to add a full block of padding in this case.
The problem with your code is that it uses the value 00
for the padding.
the encryption string in python comes out to be much shorter than encrypting the same string in C#. Thanks for any help.
I think you might be getting your encodings mixed up. Encoding.Unicode
is UTF16-LE, which uses 2 bytes per character. It would be very unusual to use this from Python: it's more likely that you're using something like UTF-8, which uses 1 byte per character for common Western characters. It's hard to say for sure without knowing how you're turning your Python string into a byte string.
With AES, the IV needs to be random unpredictable (it can be public, but it mustn't be the same as a previous thing you encrypted). It's common to randomly generate it and then add it onto the beginning of your ciphertext. Don't derive it from your key.
Upvotes: 4