После шифрования\дешифрования алгоритмом AES zip-архивы получаются битые, а в текстовых файлах пропадает небольшой кусок текста в конце

После шифровки/дешифровки zip и rar архивов они почему-то получаются битые. Так же при расшифровке текстовых файлов пропадает небольшой кусок текста в конце файла. Пробовал шифровать\дешифровать с разными настройками, но ничего не помогает. Подскажите в чем может быть дело. Для шифровки и наоборот вызываю вот такие методы:

var i = Crypton.Instance;
i.GenerateRSAKeys();
i.GenerateAesKey();
i.LoadRSAKeys();
i.LoadAesKey();
i.EncryptFiles();
i.DecryptFiles();

вот этого, своего класса:

using System;
using System.Security.Cryptography;
using System.IO;

namespace Telesyk.Cryptography
{
    public class Crypton
    {
        #region Private fields

        private static Lazy<Crypton> _instance = new Lazy<Crypton>(() => new Crypton());

        private DirectoryInfo _rootDir;
        private DirectoryInfo _workDir;
        private DirectoryInfo _encryptedDir;
        private DirectoryInfo _decryptedDir;
        private DirectoryInfo _sourceDir;

        private readonly string _PUBLIC_RSA_KEY_FILE;
        private readonly string _PRIVATE_RSA_KEY_FILE;
        private readonly string _AES_KEY_FILE;
        private readonly string _ENCRYPTED_AES_KEY_FILE;

        private const string _KEY_NAME = "telesyk";

        #endregion

        #region Constructors

        static Crypton()
        {
            
        }

        private Crypton()
        {
            initDirectories();

            _PUBLIC_RSA_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_public_key.rsa";
            _PRIVATE_RSA_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_private_key.rsa";
            _AES_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_key.aes";
            _ENCRYPTED_AES_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_encrypted_key.aes";
        }

        #endregion

        #region Public constants

        public const int RSA_KEY_SIZE = 1024;
        public const int AES_KEY_SIZE = 256;

        #endregion

        #region Public properties

        public static Crypton Instance
        {
            get { return _instance.Value; }
        }

        public byte[] PublicKey { get; private set; }

        public byte[] PrivateKey { get; private set; }

        public byte[] AesKey { get; private set; }

        public byte[] AesInitVector { get; private set; }

        public byte[] EncryptedAesKey { get; private set; }

        public byte[] EncryptedAesInitVector { get; private set; }

        #endregion

        #region Public methods

        public void GenerateRSAKeys() => generateRSAKeys();

        public void GenerateAesKey() => generateAesKey();

        public void LoadRSAKeys() => loadRSAKeys();

        public void LoadAesKey() => loadAesKey();

        public byte[] EncryptByRSA(byte[] data) => encryptByRSA(data);

        public byte[] DecryptByRSA(byte[] data) => decryptByRSA(data);

        public byte[] EncryptByAes(byte[] data) => encryptByAes(data);

        public byte[] DecryptByAes(byte[] data) => decryptByAes(data);

        public void EncryptFiles() => encryptFiles();

        public void DecryptFiles() => decryptFiles();

        #endregion

        #region Private methods

        #region Init

        private void initDirectories()
        {
            _rootDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.Parent.Parent;
            
            _workDir = initDirectory(_rootDir, "Files");
            _encryptedDir = initDirectory(_workDir, "Encrypted"); 
            _decryptedDir = initDirectory(_workDir, "Decrypted"); 
            _sourceDir = initDirectory(_workDir, "Source");

            _workDir.Create();
        }

        private DirectoryInfo initDirectory(DirectoryInfo parent, string name)
        {
            var directory = new DirectoryInfo(Path.Combine(parent.FullName, name));
            directory.Create();

            return directory;
        }

        #endregion

        #region GenerateRSAKeys

        private void generateRSAKeys()
        {
            using (var rsa = new RSACryptoServiceProvider(RSA_KEY_SIZE))
            {
                PublicKey = rsa.ExportRSAPublicKey();
                PrivateKey = rsa.ExportRSAPrivateKey();
            }

            File.WriteAllBytes(_PUBLIC_RSA_KEY_FILE, PublicKey);
            File.WriteAllBytes(_PRIVATE_RSA_KEY_FILE, PrivateKey);
        }

        #endregion

        #region GenerateAesKey

        private void generateAesKey()
        {
            using (var aes = Aes.Create())
            {
                aes.KeySize = AES_KEY_SIZE;

                aes.GenerateKey();
                aes.GenerateIV();

                AesKey = aes.Key;
                AesInitVector = aes.IV;
            }

            EncryptedAesKey = encryptByRSA(AesKey);
            EncryptedAesInitVector = encryptByRSA(AesInitVector);

            byte[] keyData = new byte[AesKey.Length + AesInitVector.Length];
            byte[] encryptedKeyData = new byte[EncryptedAesKey.Length + EncryptedAesInitVector.Length];

            AesKey.CopyTo(keyData, 0);
            AesInitVector.CopyTo(keyData, AesKey.Length);

            EncryptedAesKey.CopyTo(encryptedKeyData, 0);
            EncryptedAesInitVector.CopyTo(encryptedKeyData, EncryptedAesKey.Length);

            File.WriteAllBytes(_AES_KEY_FILE, keyData);
            File.WriteAllBytes(_ENCRYPTED_AES_KEY_FILE, encryptedKeyData);
        }

        #endregion

        #region LoadRSAKeys

        private void loadRSAKeys()
        {
            PublicKey = File.ReadAllBytes(_PUBLIC_RSA_KEY_FILE);
            PrivateKey = File.ReadAllBytes(_PRIVATE_RSA_KEY_FILE);
        }

        #endregion

        #region LoadAesKey

        private void loadAesKey()
        {
            var encryptedKeyData = File.ReadAllBytes(_ENCRYPTED_AES_KEY_FILE);

            EncryptedAesKey = new byte[128];
            EncryptedAesInitVector = new byte[128];

            encryptedKeyData.CopyTo(EncryptedAesKey, 0, 128);
            encryptedKeyData.CopyTo(EncryptedAesInitVector, 128, 128);

            AesKey = DecryptByRSA(EncryptedAesKey);
            AesInitVector = DecryptByRSA(EncryptedAesInitVector);
        }

        #endregion

        #region EncryptByRSA

        private byte[] encryptByRSA(byte[] data)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportRSAPublicKey(PublicKey, out int length);

                return rsa.Encrypt(data, false);
            }
        }

        #endregion

        #region DecryptByRSA

        private byte[] decryptByRSA(byte[] data)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportRSAPrivateKey(PrivateKey, out int length);

                return rsa.Decrypt(data, false);
            }
        }

        #endregion

        #region EncryptByAes

        private byte[] encryptByAes(byte[] data) => transformByAes(data, false);

        #endregion

        #region DecryptByAes

        private byte[] decryptByAes(byte[] data) => transformByAes(data, true);

        #endregion

        #region TransformByAes

        private byte[] transformByAes(byte[] data, bool decrypt)
        {
            using (var aes = Aes.Create())
            {
                aes.KeySize = AES_KEY_SIZE;

                if (decrypt)
                    aes.Padding = PaddingMode.None;

                aes.Key = AesKey;
                aes.IV = AesInitVector;

                using (var encryptor = decrypt ? aes.CreateDecryptor() : aes.CreateEncryptor())
                using (var memory = new MemoryStream())
                using (var stream = new CryptoStream(memory, encryptor, CryptoStreamMode.Write))
                {
                    stream.Write(data);

                    //stream.Flush();
                    //memory.Flush();

                    return memory.ToArray();
                }
            }
        }

        #endregion

        #region EncryptFiles

        private void encryptFiles()
        {
            foreach (var file in _sourceDir.GetFiles())
                encryptFile(file.FullName);
        }

        private void encryptFile(string fileName)
        {
            var encryptedFileName = Path.Combine(_encryptedDir.FullName, fileName.Substring(fileName.LastIndexOf("\\") + 1));

            var data = File.ReadAllBytes(fileName);
            var encriptedData = encryptByAes(data);

            File.WriteAllBytes(encryptedFileName, encriptedData);
        }

        #endregion

        #region DecryptFiles

        private void decryptFiles()
        {
            foreach (var file in _encryptedDir.GetFiles())
                decryptFile(file.FullName);
        }

        private void decryptFile(string fileName)
        {
            var decryptedFileName = Path.Combine(_decryptedDir.FullName, fileName.Substring(fileName.LastIndexOf("\\") + 1));

            var data = File.ReadAllBytes(fileName);
            var decryptedData = decryptByAes(data);

            File.WriteAllBytes(decryptedFileName, decryptedData);
        }

        #endregion

        #endregion
    }
}

Плюс небольшой класс с расширением для массивов:

using System;

namespace Telesyk.Cryptography
{
    public static class ArrayExtenssions
    {
        public static void CopyTo(this Array array, Array target, int startIndex, int count)
            => CopyTo(array, target, startIndex, count, 0);

        public static void CopyTo(this Array array, Array target, int startIndex, int count, int targetIndex)
        {
            for (int i = startIndex; i < startIndex + count; i++, targetIndex++)
                target.SetValue(array.GetValue(i), targetIndex);
        }
    }
}

Ответы (1 шт):

Автор решения: Barmaley

Ключ находится в том, что вы не применяете никакого алгоритма выравнивания блоков (padding), именно поэтому:

при расшифровке текстовых файлов пропадает небольшой кусок текста в конце файла

Позволю себе небольшой ликбез. Для блочных шифров (AES именно блочный) важно, чтобы размер исходного текста был кратен размеру блока. По умолчанию, размер блока AES равен 256 битам - то есть 8 байтам. Когда вы пишете:

aes.Padding = PaddingMode.None;

это равносильно тому, что вы сообщаете AES, что размер вашего текста кратен 8 байтам, в итоге это приводит к пропаданию небольшого куска в конце текста.

Вам надо явно указать алгоритм выравнивания, например:

aes.Padding = PaddingMode.PKCS7;

В этом случае к концу текста будет добавляться выравнивающие символы, которые при дешифровке будут корректно убираться. PKCS7 выравнивает до полного блока добавляя в конце блока байты содержащие количество байтов (в примере добавлено 4 байта 04):

... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

→ Ссылка