1

I've been banging my head against the wall for a couple of days now and was hoping someone could point out the obvious. I am trying to match the RijndaelManaged encryption of C# with that of Python using Crypto.Cipher.

Problem: Despite everything being equal I am receiving two different encrypted outputs.

These are the values passed:

  • encryption key as string (32 characters)
  • value to be encrypted already padded as string (in this case 32 characters)

Python 3:

import os
import base64
from Crypto.Cipher import AES

iv = 'wdp0hP2WRKWsuP8B'

def fix_binary_data_length(binary_value):
  block_length = 16
  binary_value_length = len(binary_value)
  length_with_padding = (
    binary_value_length + (block_length - binary_value_length) % block_length
  )
  return binary_value.ljust(length_with_padding, b'=')

def encrypt(value: str, key: str):
  binary_iv = iv.encode('UTF-8')
  binary_value = value.encode('UTF-8')
  binary_value = fix_binary_data_length(binary_value)
  binary_key = key.encode('UTF-8')
  cipher = AES.new(binary_key, AES.MODE_CBC, binary_iv)
  encrypted_value = cipher.encrypt(binary_value)
  return encrypted_value

C#

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

public class Crypt {

  public static string encrypt(string _sValue, string _sKey){
    string _sIv = "wdp0hP2WRKWsuP8B";
    if (_sKey.Length != 32) { return string.Empty; }
    using (RijndaelManaged _Rijndael = new RijndaelManaged(){
      Mode = CipherMode.CBC,
      KeySize = 256,
      BlockSize = 128
    }){
      byte[] _chEncrypt = Encoding.UTF8.GetBytes(_sValue);
      using (ICryptoTransform _Encryptor = _Rijndael.CreateEncryptor(Encoding.UTF8.GetBytes(_sKey), Encoding.UTF8.GetBytes(_sIv)))
      using (MemoryStream _Stream = new MemoryStream())
      using (CryptoStream _CryptoStream = new CryptoStream(_Stream, _Encryptor, CryptoStreamMode.Write)){
        _CryptoStream.Write(_chEncrypt, 0, _chEncrypt.Length);
        _CryptoStream.FlushFinalBlock();
        return Convert.ToBase64String(_Stream.ToArray());
      }
    }
    return string.Empty;
  }
}
Sylver11
  • 129
  • 1
  • 8
  • Your C# code won't compile -- you can't return `false` from a method which returns `string`. If you haven't even run the code in your question, there's no point in us spending time going through it line-by-line. Please post an **actual** [mcve] which we -- and **you** -- can run. – canton7 Aug 12 '21 at 08:48
  • @canton7 my apologies I just updated the code. – Sylver11 Aug 12 '21 at 09:25
  • 1
    The generated ciphertexts are trivially different, because you use random IVs. Apart from that you apply different paddings. Btw, the IV derivation is not plausible (truncation of the Base64 encoded data). – Topaco Aug 12 '21 at 10:07
  • @Topaco thanks for your response. Regarding the IVs I included the IV generating part for context reasons but when testing I actually pass the same 16 character IV string. The output I get from each one of them is somewhat similar. The python encrypted string: `H5DGbNhnJUT/apqzgOvAr2qRG78TgffIrPKhLQMA6T8=` and the C# encrypted string: `H5DGbNhnJUT/apqzgOvAr/rW+2OxvCq7FVMRWuN/YVs=` At about half way they deviate.. does that have to do with the IV? – Sylver11 Aug 12 '21 at 10:28
  • 1
    No, that's the different padding (custom in the Python code, PKCS7 in the C# code). Apply the same padding, preferably PKCS7. PyCryptodome supports padding in a dedicated module, [`Crypto.Util.Padding`](https://pycryptodome.readthedocs.io/en/latest/src/util/util.html#crypto-util-padding-module). – Topaco Aug 12 '21 at 10:33

1 Answers1

1

Credits to @Topaco for pointing out the obvious ;)

Seems like I did not fully grasp the concept of padding. This is how I got it to work:

import os
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

iv = 'wdp0hP2WRKWsuP8B'

def encrypt(value: str, key: str):
  binary_iv = iv.encode('UTF-8')
  binary_value = value.encode('UTF-8')
  binary_key = key.encode('UTF-8')
  cipher = AES.new(binary_key, AES.MODE_CBC, binary_iv)
  encrypted_value = cipher.encrypt(pad(binary_value, AES.block_size))
  encrypted_value = base64.b64encode(encrypted_value)
  return encrypted_value
Sylver11
  • 129
  • 1
  • 8