Шифровать/дешифровать поток на C# с помощью RijndaelManaged

Я пытаюсь зашифровать общий поток на С#. Хотя у программы нет проблем, шифрование и дешифрование возвращают пустые значения при преобразовании в строки. Любая помощь приветствуется.

    public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
        {
            byte[] encryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.Close();
                    }
                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
        {
            byte[] decryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                        cs.Close();
                    }
                    decryptedBytes = ms.ToArray();
                }
            }

            return decryptedBytes;
        }

        private void Encrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Encrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        private void Decrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Decrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        static void Main(string[] args)
        {

            Program obj = new Program();
            var message = new MemoryStream();
            var cipher = new MemoryStream();
            string tmp = "This is a test if the encryption is working!";

            StreamWriter sw = new StreamWriter(message);
            sw.Write(tmp);

            obj.Encrypt(message, cipher, "password");

            cipher.Position = 0;
            message = new MemoryStream();

            obj.Decrypt(cipher, message, "password");

            using (var memoryStream = new MemoryStream())
            {
                message.CopyTo(memoryStream);
                var bytesdecrypt = memoryStream.ToArray();
                string result = Encoding.UTF8.GetString(bytesdecrypt);
                Console.WriteLine(result);
                Console.ReadLine();
            }


        }
    }
}

Проблема, вероятно, заключается в том, что я читаю и пишу из и в потоки.


person John Doe    schedule 07.06.2016    source источник
comment
Проверьте это: stackoverflow.com/questions /202011/зашифровать-и-расшифровать-строку/   -  person Frank Bryce    schedule 07.06.2016
comment
Что вы имеете в виду под пустым? Как вы проверяете, есть ли там что-то?   -  person Artjom B.    schedule 08.06.2016
comment
Джонни Д. Вот более подходящая ссылка для вашей конкретной проблемы: stackoverflow. ком/вопросы/22145836/   -  person mikey    schedule 08.06.2016
comment
После расшифровки я получаю пустую строку.   -  person John Doe    schedule 08.06.2016
comment
То есть шифрование не пустое, верно?   -  person Artjom B.    schedule 08.06.2016
comment
Проблема в потоке IO, шифрование довольно стандартное   -  person John Doe    schedule 08.06.2016
comment
Рабочий пример с использованием строки и фразы-пароля: selamigungor.com /post/7/encrypt-decrypt-a-string-in-csharp — не решает проблему с потоком, когда нужно просто сбросить позицию.   -  person vapcguy    schedule 12.02.2020


Ответы (1)


С этим кодом много проблем.

  1. Причина, по которой ничего не расшифровывается, заключается в том, что вы забыли сбросить поток message перед выполнением message.CopyTo(memoryStream), потому что CopyTo работает с текущей позиции, а вы не изменили позицию после расшифровки.

    Вы можете сбросить его с помощью

    message.Position = 0;
    
  2. Если зашифрованы произвольные данные, AES с каким-то режимом работы вроде CBC недостаточно. Обычно нам нужна какая-то схема заполнения. В C# схема по умолчанию — заполнение PKCS#7. Однозначное заполнение всегда добавляется, даже если открытый текст уже кратен размеру блока. В этих случаях добавляется полный блок заполнения.

    Теперь проблема в том, что вы читаете фрагменты по 2048 байт во время шифрования и дешифрования, но шифрование создает фрагменты зашифрованного текста по 2064 байта, которые должны быть прочитаны как таковые во время дешифрования. Это простое исправление, но было бы лучше использовать потоки полностью, а не шифровать такие отдельные куски.

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

person Artjom B.    schedule 07.06.2016
comment
Спасибо @Artjom B.! - person John Doe; 08.06.2016
comment
Пример с дополнением PKCS # 7 был бы оценен. Критиковать приятно, но без кода, демонстрирующего концепцию, это не очень полезно. Пример того, как рандомизировать соль, тоже был бы полезен. - person vapcguy; 12.02.2020