Шифрование AES с использованием C# - PullRequest
1 голос
/ 08 января 2020

Я новичок ie в криптографии, и чтобы узнать его, я попытался зашифровать / расшифровать с AES в C#.

К сожалению, я понял, что это не так просто, как Я думал. Поэтому я искал более простое решение.

Позже я нашел пару фрагментов кода , включая некоторые пояснения.

Я скопировал код и попытался внедрить его в небольшое приложение.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace aes
{
    class Program
    {

        public static string passwd = null;
        public static string content = null;
        public static string encryptedcontent = null;

        public static byte[] IV = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
        public static int BlockSize = 128;


        static void Encrypt()
        {

            if (passwd == "") return;
            //Content to Byte Array
            byte[] bytes = Encoding.Unicode.GetBytes(content);
            //Encrypt
            //Init AES
            SymmetricAlgorithm crypt = Aes.Create();
            //Init md5 hash
            HashAlgorithm hash = MD5.Create();
            //AES blocksize (AES 192 etc.) (min 128)
            crypt.BlockSize = BlockSize;
            //Generating Key
            crypt.Key = hash.ComputeHash(Encoding.Unicode.GetBytes(passwd));
            //Initialize Vectors
            crypt.IV = IV;

            //CryptoStram is used for encryption
            //The required Encryptor is based on the algorithm above
            //Cryptostream sends data of the encrypted byte array to Memorystream
            //The memory stream is then converted into a Base64 string and made readable
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream =
                   new CryptoStream(memoryStream, crypt.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(bytes, 0, bytes.Length);
                }

                encryptedcontent = Convert.ToBase64String(memoryStream.ToArray());
            }
        }


        static void Main(string[] args)
        {
//Set Password
            Console.WriteLine("Passwort angeben");
            Console.Write("> ");
                 passwd = Console.ReadLine();

//Set content to encrypt (String)
            Console.WriteLine("Zu verschlüsselner Text angeben");
            Console.Write("> ");
                 content = Console.ReadLine();


            Encrypt();

            Console.WriteLine(encryptedcontent);

            Console.ReadLine();
        }
    }
}

Впоследствии я хотел попробовать программу с некоторыми тестовыми данными. Я на самом деле получил зашифрованную строку.

PW: supersecretpassword Содержание: I like to keep my secrets Результат: SEEc1sLMIyfVFsoHPFRIcl437+yjUC5uFMgco3iO+oWSgJWQOwKhoDhUbFJREeqiIvaY2DBR+Ih4OJeGAc6JZQ==

Я пытался использовать некоторые онлайн-инструменты для расшифровки и проверки своего результата. К сожалению, большинство веб-инструментов не смогли расшифровать мой результат.

И если я зашифрую предложение I like to keep my secrets с помощью этих онлайн-инструментов, я получу результаты типа: 7IWuebm0T8HdrGdtkBjt5zgjbdEqYfidNZVvfgtOjH4=

Мой результат SEEc1sLMIyfVFsoHPFRIcl437+yjUC5uFMgco3iO+oWSgJWQOwKhoDhUbFJREeqiIvaY2DBR+Ih4OJeGAc6JZQ==

Как видите, два результата разные. К сожалению, я понятия не имею, почему это может иметь место.

Спасибо за вашу помощь

Jonas

PS Каким-то образом я удалил некоторые строки, написанные в этом вопросе. Я надеюсь, что новые слова могут прояснить, в чем моя проблема.

Ответы [ 2 ]

2 голосов
/ 08 января 2020

Вы не говорите, что онлайн-инструментам удалось или не удалось воспроизвести ваши результаты, так что это общий ответ вместо указания c.

//AES blocksize (AES 192 etc.) (min 128)
crypt.BlockSize = BlockSize;

BlockSize для AES равен 128. Всегда (в отличие от исходного алгоритма Rijndael, который позволяет изменять BlockSize).

AES-128 / AES-192 / AES-256 относятся к KeySize, а не к BlockSize.

crypt.Key = hash.ComputeHash(Encoding.Unicode.GetBytes(passwd));

Вы используете MD5(UTF16(password)) в качестве своей функции получения ключа (KDF). Может быть, вы можете найти онлайн-образец, который использует это, но они с большей вероятностью будут использовать MD5(UTF8(password)) (что будет от Encoding.UTF8, против Encoding.Unicode). Лучшим ответом было бы использование правильной функции получения ключа на основе пароля, такой как PBKDF2 (которая вызывается Rfc2898DeriveBytes in. NET по ... причинам).

[Когда я шифрую I like to keep my secrets Я получаю ответ, который вдвое длиннее онлайновых инструментов.]

Вы шифруете представление этой строки в формате UTF-16. Строка состоит из 25 значений кодовой точки Unicode, все из диапазона US-ASCII. Таким образом, представление UTF-16 - это просто длина кодовой точки * 2 (50 байтов).

50 байтов разбивается на 3 16-байтовых (128-битных) блока, плюс 2 оставшихся байта. Добавьте заполнение, которое станет 4 блоками вывода AES-CB C -PKCS # 7 (64 байта). 64 байта преобразуются в Base64 как 21 полное значение (из 3 байтов -> 4 символов) с оставшимся 1 байтом, поэтому значение Base64 заканчивается 2 = дополнительными символами общей длиной 88 символов. Это соответствует вашему описанию, hooray:).

Если, с другой стороны, вы использовали кодировку UTF-8, у вас будет 25 байтов для шифрования, что станет 2 выходными блоками (32 байта), которые превращается в 10 полных преобразований в base64 с оставшимися 2 байтами, поэтому один = в сумме составляет 44 символа ... что очень похоже на то, что используют онлайн-инструменты.

Вы также должны создать новый IV за каждый раз, когда вы шифруете одним и тем же ключом. IV не является ключом, но изменение IV приводит к тому, что один и тот же секретный ввод шифруется по-разному, поэтому тот, кто может видеть ваши зашифрованные данные, не может сказать, что вы отправили то же сообщение, которое вы только что отправили. (По крайней мере, это является целью в режиме блока CB C, в других режимах блока это имеет иногда более важные цели). IV может быть передано с сообщением ... на самом деле так и должно быть, если только у вас нет другого способа согласиться с обеими сторонами (без жесткого кодирования).

И, конечно, вы должны распоряжаться всеми из ваших одноразовых предметов. Если изменить кодировку на UTF-8, но не менять KDF, лучше будет

private static string Encrypt(string content, string password)
{
    byte[] bytes = Encoding.UTF8.GetBytes(content);

    using (SymmetricAlgorithm crypt = Aes.Create())
    using (HashAlgorithm hash = MD5.Create())
    using (MemoryStream memoryStream = new MemoryStream())
    {
        alg.Key = hash.ComputeHash(Encoding.UTF8.GetBytes(password));
        // This is really only needed before you call CreateEncryptor the second time,
        // since it starts out random.  But it's here just to show it exists.
        alg.GenerateIV();

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

        string base64IV = Convert.ToBase64String(crypt.IV);
        string base64Ciphertext = Convert.ToBase64String(memoryStream.ToArray());

        return base64IV + "!" + base64Ciphertext;
    }
}
0 голосов
/ 08 января 2020

Некоторые проблемы, которые я вижу, это самоопределяемые IV и нечетные blocksize, edit: и вы, вероятно, имеете в виду неверное значение для пароля при сравнении с онлайн-инструментами, где вам нужно ввести пароль как вычисляется функцией ComputeHa sh.

Проверьте этот простой MSDN Пример

...