A customer has provided sample code in C# as an encryption spec. My code is in Python 2.7, and I have to make my data match theirs. My data matches an online calculator but not the customer data. To make a demo, I have reduced everything to zero data, keys and IVs.
This is the C#, for .Net 4.2.7:
private static void TestEncrypt2()
{
byte[] bytesToEncrypt = new byte[] { 0x00, 0x00, 0x00, 0x00 };
byte[] encrypted = null;
using (var cryptoService = Aes.Create())
{
cryptoService.Key = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
cryptoService.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
using (ICryptoTransform encryptor = cryptoService.CreateEncryptor(cryptoService.Key, cryptoService.IV))
{
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(bytesToEncrypt, 0, bytesToEncrypt.Length);
csEncrypt.FlushFinalBlock();
encrypted = msEncrypt.ToArray();
}
}
}
}
DumpByteArray(encrypted);
}
Resulting in this output:
Test Encrypt 2, zero data, key and IV: B5 67 D8 36 7C FD F3 7A 89 8F 18 3A 49 83 7A D3
This is Python 2.7:
from Crypto.Cipher import AES
...
def SREncrypt0():
packedIv = ByteListToPackedString([0]*16)
packedKey = ByteListToPackedString([0]*32)
c = AES.new(packedKey, AES.MODE_CBC, packedIv)
packedClr = ByteListToPackedString(HexStringToByteList("00000000"))
while 0 != (len(packedClr) % 16):
packedClr += struct.pack("B", 0) #4) #PKCS7 padding for 4 byte payload
return PackedStringToHexString(c.encrypt(packedClr))
#result DC95C078A2408989AD48A21492842087 agrees with http://aes.online-domain-tools.com/
Resulting in:
DC95C078A2408989AD48A21492842087
An online calculator agrees with Python:
Is C# AES-256 known bad, or am I doing something wrong? In each case, I am trying to encrypt a 4 byte cleartext with a 16 byte IV and 32 byte key, all zeroes. Seems like it should be the "lowest common denominator," but various implementations disagree. How do you figure out the truth in this situation? Should I upgrade .Net levels, or import BouncyCastle? Either option takes at least a few hours.
In PKCS#7 padding, the padding bytes aren't zero. Instead, if N padding bytes are needed, then the value of each byte is N. (Examples at Wikipedia).
So in this case, the 4-byte plaintext needs 12 bytes of padding to make a 128-bit block. Each padding byte has the value denary 12, i.e. hex 0C
.
The padded plaintext should be 000000000C0C0C0C0C0C0C0C0C0C0C0C
. If you try that as the plaintext in the online tool you showed, with zero key and IV, you get the same B5 67 D8 36 7C FD F3 7A 89 8F 18 3A 49 83 7A D3
that the C# code returned.
So in your Python code, the padding logic should be something like:
n_padding = 16 - len(packedClr) % 16
for _ in range(n_padding):
packedClr += struct.pack("B", n_padding)