Как превратить функцию python AES в код на C#

#python #c# #aes

Вопрос:

У меня есть часть кода python, которую я хочу использовать в своем проекте на C#, но я не могу найти правильный способ ее достижения.

код на python:

 def getCiphertext(plaintext, key = key_, cfb_iv = iv_, size = 128):
    message = plaintext.encode('utf-8')
    cfb_cipher_encrypt = AES.new(key, AES.MODE_CFB, cfb_iv, segment_size = size) 
    mid = cfb_cipher_encrypt.encrypt(message)
    return hexlify(mid).decode()
 

Я попробовал приведенный ниже код на C#, но результат другой:

 using System.Security.Cryptography;
public static string AesEncrypt(string str, string key, string IVString)
{
    Encoding encoder = Encoding.UTF8;
    byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
    RijndaelManaged rm = new RijndaelManaged
    {
        Key = encoder.GetBytes(key),
        Mode = CipherMode.CFB,
        BlockSize = 128,
        Padding = PaddingMode.PKCS7,
        IV = encoder.GetBytes(IVString),
    };
    ICryptoTransform cTransform = rm.CreateEncryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
    return ToBCDStringLower(resultArray);//result
}
    
public static string ToBCDStringLower(byte[] buffer)
{
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < buffer.Length; i  )
    {
        sb.Append(buffer[i].ToString("x2"));
    }
    return sb.ToString();
}
 

Спасибо всем ребятам!

Ответ №1:

Реализация CFB в .NET:

CFB в .NET проблематичен. В .NET Framework это поддерживается, в .NET Core только с .NET 5.0.

Кроме того, .NET Framework и .NET Core допускают разные размеры сегментов, но оба поддерживают 8-разрядные и 128-разрядные, что соответствует наиболее распространенным вариантам, а именно CFB8 и CFB128 (или CFB полного блока). Размер сегмента является дополнительным параметром в CFB, который соответствует битам, зашифрованным на каждом шаге шифрования, см. CFB.

Еще одна особенность заключается в том, что в .NET размер открытого текста должен быть целым числом, кратным размеру сегмента. Это замечательно (на самом деле уже ошибка), так как CFB-это режим потокового шифрования, который не требует заполнения.
Поэтому для CFB, за исключением CFB8, обычно требуется заполнение. В случае CFB128 на весь блок, позволяя применять заполнение по умолчанию PKCS7.
Таким образом, чтобы получить зашифрованный текст, соответствующий незашифрованному открытому тексту, зашифрованный текст должен быть усечен до размера открытого текста.

Сравнение кода на Python и C# :

В опубликованном коде Python размер сегмента в списке аргументов по умолчанию равен 128 битам (по умолчанию PyCryptodome равен 8 битам). В коде C# размер сегмента (здесь обозначается как FeedbackSize ) не указан, поэтому используется значение по умолчанию 128 бит.
Таким образом, если в коде Python явно не указан размер сегмента, отличный от 128 бит, оба кода применяются к одному и тому же размеру сегмента.
Кроме того, в коде C# заполнение (PKCS7) выполняется в соответствии с требованиями реализации C#. Поэтому, когда зашифрованный текст кода C# усекается до размера открытого текста, он совпадает с зашифрованным текстом кода Python.

В следующем примере используется код, который вы опубликовали без изменений:

 string plaintext = "The quick brown fox jumps over the lazy dog";
string key = "01234567890123456789012345678901";
string cfb_iv = "0123456789012345";
string ciphertext = AesEncrypt(plaintext, key, cfb_iv);
string ciphertextTrunc = ciphertext.Substring(0, plaintext.Length * 2); // *2 since AesEncryptOP returns the ciphertext hex encoded
Console.WriteLine(ciphertext);
Console.WriteLine(ciphertextTrunc);
 

Выход:

 09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66d268c6d1ad
09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66
 

Как вы можете убедиться, сокращенный зашифрованный текст соответствует выходу кода Python.

Обратите внимание, что, как объяснено в 1-м разделе, для CFB128 требуется заполнение. Изменение заполнения на PaddingMode.None приведет к исключению CryptographicException: входные данные не являются полным блоком. Однако с CFB8 это было бы возможно.


Вышибала:

Альтернатива .Встроенная реализация NET-это BouncyCastle, которая реализует CFB в режиме потокового шифрования, так что заполнение не требуется. Следующий код:

 using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
...
public static string Encrypt(string str, string keyString, string IVString)
{
    byte[] inputBytes = Encoding.UTF8.GetBytes(str);
    byte[] IV = Encoding.UTF8.GetBytes(IVString);
    byte[] key = Encoding.UTF8.GetBytes(keyString);

    AesEngine engine = new AesEngine();
    CfbBlockCipher blockCipher = new CfbBlockCipher(engine, 128);
    BufferedBlockCipher cipher = new BufferedBlockCipher(blockCipher);
    KeyParameter keyParam = new KeyParameter(key);
    ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);

    cipher.Init(true, keyParamWithIv); 
    byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)]; 
    int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
    cipher.DoFinal(outputBytes, length);
    string encryptedInput = ToBCDStringLower(outputBytes);

    return encryptedInput;
}
 

напрямую (т. е. без усечения) возвращает результат кода Python.