Search code examples
c#pythonencryptionaesbouncycastle

Issue with encrypting in C# using Bouncy Castle and decrypting in Python using AES (EAX Mode)


I'm attempting to encrypt text in C# and decrypt it in python using the EAX Mode in AES. I'm using Bouncy Castle for EAX in C# and AES for Python.

I'm able to successfully encrypt and decrypt in both C# and Python, however I've noticed that when C# encrypts the the text, the output is significantly longer than when Python encrypts it.

Not sure if it's relevant, but I'm sending it from C# to Python over a server, and I confirmed that everything is sent as it should be. The client is running an Android emulator while the server is running Windows 10.

The method I'm using to test the C# code:

const int MAC_LEN = 16
//The Key and Nonce are randomly generated
AeadParameters parameters = new AeadParameters(key, MAC_LEN * 8, nonce);

string EaxTest(string text, byte[] key, AeadParameters parameters)
{
    KeyParameter sessKey = new KeyParameter(key);
    EaxBlockCipher encCipher = new EAXBlockCipher(new AesEngine());
    EaxBlockCipher decCipher = new EAXBlockCipher(new AesEngine());

    encCipher.Init(true, parameters);
    byte[] input = Encoding.Default.GetBytes(text);
    byte[] encData = new byte[encCipher.GetOutputSize(input.Length)];
    int outOff = encCipher.ProcessBytes(input, 0, input.Length, encData, 0);
    outOff += encCipher.DoFinal(encData, outOff);

    decCipher.Init(false, parameters);
    byte[] decData = new byte[decCipher.GetOutputSize(outOff)];
    int resultLen = decCipher.ProcessBytes(encData, 0, outOff, decData, 0);
    resultLen += decCipher.DoFinal(decData, resultLen);
    return Encoding.Default.GetString(decData);
}

The method I'm using to test the python code:

def encrypt_text(data, key):
    cipher = AES.new(key, AES.MODE_EAX)
    nonce = cipher.nonce
    cipher_text, mac_tag = cipher.encrypt_and_digest(data)
    return [cipher_text, mac_tag, nonce]


def decrypt_text(data, key, mac_tag, nonce):
    decrypt = AES.new(key, AES.MODE_EAX, nonce=nonce, mac_len=16)
    plaintext = decrypt.decrypt_and_verify(data, mac_tag)
    return plaintext

For a test of the string "a", in C#, I consistently get an encrypted text of 17 bytes and with python I consistently get an encrypted text of 1 byte. When I try to decrypt in python, I get this Error [ValueError: MAC check failed]. Both the Mac and nonce are consistently 16 bytes.

Example C# Output: 34 2D 0A E9 8A 37 AC 67 0E 95 DB 91 D7 8C E5 4E 9F

Example Python Output: DD


Solution

  • The default encoding in C# is UTF-16LE, which should give you two bytes of plaintext and therefore two bytes of ciphertext. However, in the C# / Bouncy Castle code the returned ciphertext contains the authentication tag of 16 bytes at the end. Obviously you're missing one byte, 17 bytes is one byte short. So the transport of the ciphertext has failed somewhere. Of course, in that case, the verification of the authentication tag will also fail.

    In Python, the ciphertext is one byte and the authentication tag is 16 bytes. This is correct for a single byte of input. Your encoding is not in the code fragments given, but I presume it is one byte in UTF-8.

    Make sure you use UTF-8 also for your C# code and make sure that the ciphertext is correctly transported. Make sure you use base 64 where transport over a text interface is required and don't skip zero valued bytes. Finally, if you use a random nonce, make sure you transport it with the ciphertext (usually it is prefixed). After all that you should be OK.