0

I have been given an existing encryption algorithm that is required to encrypt a password before sending it on an API.

I have no previous experience of any other languages than Python and so I am unable to understand what functionally I need to do to replicate this.

I believe the default mode for AES in C# is CBC. I think I've copied most of the work needed but I need to pad the data, and I don't know exactly what stage this occurs, or where the length is added. I don't understand the order things would happen in the C code. I also believe the default padding method is PKCS#7, though I'm happy to be corrected on any of this.

Original code

public static string EncryptStringToBytes_Aes(string username, string password)
{
    string encrypted = string.Empty;
    byte[] clearBytes = Encoding. UTF8.GetBytes(password);
    using (Aes aesAlg = Aes.Create())
    {
        byte[] k;
        byte[] iv;
          byte[] bytes = Encoding.UTF8.GetBytes(username);
        k = SHA256.Create().ComputeHash(bytes);
        iv = MD5.Create().ComputeHash(bytes);
        aesAlg.Key = k;
        aesAlg.IV = iv;

        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, 
        aesAlg.IV);

        using (MemoryStream msEncrypt = new MemoryStream())
        {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, 
                encryptor, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(clearBytes, 0, clearBytes.Length);
                }
                encrypted = Convert.ToBase64String(msEncrypt.ToArray());
        }
    }
    return encrypted;
}
python recreation

from Crypto.Cipher import AES
import hashlib
username = "example"
password = "example2"
mode = AES.MODE_CBC
clearbytes = password.encode('utf-8')
bytes = username.encode('utf-8')
key = hashlib.sha256(bytes).digest()
iv = hashlib.md5(bytes).digest()

encryptor = AES.new(key, mode, IV = iv)
length = password + '0' + str(len(clearbytes))
encrypted= encryptor.encrypt(length).encode('base64')

I'm getting a "ValueError: Input strings must be a multiple of 16 in length".

And when I've tried some padding, the encode states "AttributeError: 'bytes' object has no attribute 'encode'"

I'm hoping that someone who can read this code can help me with the final steps to recreate the functionality in Python.

2
  • You can find padding functions in Crypto.Util.Padding, or if you're using PyCrypto you can copy PyCryptodome's padding functions from github. However your C# code doesn't look good: It's using a hash as a kdf, deterministic IV, and no authentication. Those factors may reduce or even break the security of your encryption scheme. Commented May 2, 2019 at 17:56
  • Thanks for the help! And yes, I'm aware the encryption isn't great, its an external companies API so we have to jump through the hoops as required. Fortunately its not something we particularly need to keep secure: we just need it to work! Commented May 3, 2019 at 8:10

2 Answers 2

1

Looking at the docs, you are not using the Cipher module correctly.

When calling AES.new(), the key that you pass in needs to be a 16 character byte string, so you need to pad your key attribute to have a length of 16 bytes.

The encrypt() method take a byte string as input, where as you appear to be trying to pass in a length (which isn't even defined in your example).

Your comments suggest you made it passed though, as otherwise you wouldn't have seen the AttributeError exception.

The AttributeError you're seeing is because Cipher.encrypt() returns a byte array, and a byte array does not have an encode() method. You need to first convert your byte array to a string. The following code snippet from here should help:

import array
decoded = array.array('b', your_input).tostring().decode('utf-8') 

You should obviously replace 'utf-8' with 'base64'.

Sign up to request clarification or add additional context in comments.

3 Comments

Apologies, I missed out this line. length = password + '0' + str(len(clearbytes))
That looks good, that should fix the output, I'm hoping that everything just needs PKCS#7 padding but the original code line csEncrypt.Write(clearBytes, 0, clearBytes.Length) worries me, as I'm not sure exactly what its doing!
Suggesting pad the key is not good advice. One should say generate a random 16-byte key for AES-128. Possibly one can generate from a password by using PBKDF functions as Argon2, Bcrypt, and PBKDF2.
1

I believe I have been successful, using a combination of the answers and comments here. Padding the key was not required as the hash is already a multiple of 16. The (hopefully) successful code is below for reference. (I have two other sections of C# code to translate before I can even test connecting unfortunately!)

Python Conversion

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import hashlib
import base64


username = "example"
password = "example"
mode = AES.MODE_CBC
bytes = username.encode('utf-8')
data_for_padding = password + '0' + str(len(password))
padded = pad(data_for_padding)

key = hashlib.sha256(bytes).digest()
iv = hashlib.md5(bytes).digest()
encryptor = AES.new(key, mode, IV = iv)
clearbytes = padded.encode('utf-8')
ciphertext = encryptor.encrypt(clearbytes)

result = base64.b64encode(ciphertext)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.