Python AES encryption shows different result from originating C# code

Question:

The below code is my C# code for encryption

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

  public static string Encrypt(string value)
        {
            byte[] bytes = Encoding.Unicode.GetBytes(value);

            Aes aes = Aes.Create();

            Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes("aaaabbbbccccddddffffeeee", new byte[13] { 73, 118, 97, 110, 32, 77, 101, 100, 118, 101, 100, 101, 118 });


            aes.Key = rfc2898DeriveBytes.GetBytes(32);
            aes.IV = rfc2898DeriveBytes.GetBytes(16);


            MemoryStream memoryStream = new MemoryStream();
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cryptoStream.Write(bytes, 0, bytes.Length);
                cryptoStream.Close();
            }

            value = Convert.ToBase64String(memoryStream.ToArray());
            return value;
        }

the result of it for

Console.WriteLine(Encrypt("hello"));

is the below one

t2FQeBN/POo+mNW5MgIC4Q==

Here is my Python code for equivalent of C# above,

from Crypto.Protocol.KDF import PBKDF2
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

password = "aaaabbbbccccddddffffeeee"
salt = bytes([73, 118, 97, 110, 32, 77, 101, 100, 118, 101, 100, 101, 118])

def encrypt(value):
    handledByte = value.encode()

    key = PBKDF2(password, salt, 32)
    iv = bytes([204, 21, 82, 91, 78, 119, 26, 182, 218, 43, 182, 239, 169, 70, 216, 137])

    cipher = AES.new(key, AES.MODE_CBC, iv=iv)

    raw = pad(handledByte, AES.block_size)
    ciphered_data = cipher.encrypt(raw)

    return(base64.b64encode(iv + ciphered_data).decode())

 

the result of it for

print(encrypt("hello"));

is the below one

zBVSW053GrbaK7bvqUbYidcdcXlpDBKMqq8EGqN2eac=

I am aware that I am missing something but I could not find it anyway. I’d appreciate any help.

Asked By: Eymen Efe Altun

||

Answers:

Regarding the plaintext, different encodings are used, UTF-16LE in the C# code (called Unicode) and UTF-8 in the Python code (the encode() default). For UTF-16LE in Python, value.encode('utf-16le') must be used.
Also, the C# code derives the IV using PBKDF2, whereas the Python code applies a hard-coded IV (which is the same as the IV that was derived, but of course this only applies to these specific PBKDF2 parameters and is generally incorrect).
And the Python code concatenates IV and ciphertext, while the C# code does not (which is not necessary, as the IV is derived using PBKDF2).

If this is corrected, the Python code will give the same result as the C# code:

from Crypto.Protocol.KDF import PBKDF2
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

password = "aaaabbbbccccddddffffeeee"
salt = bytes([73, 118, 97, 110, 32, 77, 101, 100, 118, 101, 100, 101, 118])

def encrypt(value):
    handledByte = value.encode('utf-16le') # Fix 1: apply UTF-16LE

    keyIv = PBKDF2(password, salt, 32+16) # Fix 2: Derive key and IV
    key = keyIv[:32]
    iv = keyIv[32:]

    cipher = AES.new(key, AES.MODE_CBC, iv=iv)

    raw = pad(handledByte, AES.block_size)
    ciphered_data = cipher.encrypt(raw)

    return(base64.b64encode(ciphered_data).decode()) # Fix 3: Don't concatenate IV and ciphertext

print(encrypt('hello')) # t2FQeBN/POo+mNW5MgIC4Q==

Both codes use the default values for the iteration count and the HMAC digest. This works because both libraries apply the same values, namely 1000 and SHA-1, but it is more robust to specify the values explicitly, see Rfc2898DeriveBytes() for the C# code and PBKDF2 for the Python code.


Security: 1000 is generally far too small for the iteration count for PBKDF2. A few 100000 is typical (generally: as high as possible with acceptable performance).
A static salt is a vulnerability. Usually a random salt is generated for each encryption. This is not secret and is passed together with the ciphertext (generally concatenated).

Answered By: Topaco