robocip93
@robocip93

C# Как зашифровать файл?

Здравствуйте!
Есть файл, который нужно зашифровать методом перестановки по ключу. Делаю парой ключ-значение (Dictionary), где ключ - это буква ключа, значение - List из байтов. Проблема в том, что для целостности картины нужно чтобы число байтов было кратным числу символов в ключе. Не могу сообразить как добавить нули в начало файла.

Пример моего кода шифрования для текста:
code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace coding_encoding
{

    class Crypto
    {
        private string key;
        private char[] temp;

        public Crypto(string key)
        {
            this.key = key;
            temp = this.key.ToCharArray();
            Array.Sort(temp);
        }

        /// <summary>
        /// Метод, позволяющий расшифровать строку по ключу. 
        /// </summary>
        /// <param name="message">Строка, которую нужно расшифровать.</param>
        /// <param name="key">Ключ, по которому будет производиться расшифровка.</param>
        /// <returns>Расшифрованная строка.</returns>
        public string Decrypt(string message)
        {
            string sortedKey = string.Empty;
            string result = string.Empty;

            Dictionary<int, char> dictionaryIntChar = new Dictionary<int, char>();
            Dictionary<char, List<char>> keyValuePairs;
            Dictionary<char, List<char>> resultDictionary = new Dictionary<char, List<char>>();

            for (int i = 0; i < key.Length; i++)
                dictionaryIntChar[i] = key[i];

            foreach (char ch in temp)
                sortedKey += ch;
            //здесь идет добавление символа для кратности
            while (message.Length % key.Length != 0)
                message += " ";

            keyValuePairs = GetDictionary(message, sortedKey);

            for (int i = 0; i < key.Length; i++)
            {
                for (int j = i; j < message.Length; j += key.Length)
                {
                    resultDictionary[dictionaryIntChar[i]] = keyValuePairs[dictionaryIntChar[i]];
                }
            }
            //компановка полученного сообщения
            return Composition(key.ToCharArray(), resultDictionary);
        }

        /// <summary>
        /// Метод, позволяющий зашифровать строку по ключу.
        /// </summary>
        /// <param name="message">Строка, которую нужно зашифровать.</param>
        /// <param name="key">Ключ, по которому будет производиться шифрование.</param>
        /// <returns>Зашифрованная строка.</returns>
        public string Encrypt(string message)
        {
            Dictionary<char, List<char>> keyValuePairs;
            string result = string.Empty;

            //добавляем в строку пустые символы для сохранения целостности столбцов
            while (message.Length % key.Length != 0)
                message += " ";

            keyValuePairs = GetDictionary(message, key);
            //компановка полученного сообщения
            return Composition(temp, keyValuePairs);
        }

        /// <summary>
        /// Компановка полученного сообщения.
        /// </summary>
        /// <param name="editKey">Массив из букв ключа.</param>
        /// <param name="keyValuePairs">Словарь ключ-значение</param>
        /// <returns>Скомпонованное сообщение.</returns>
        private string Composition(char[] editKey, Dictionary<char, List<char>> keyValuePairs)
        {
            string result = string.Empty;
            for (int i = 0; i < keyValuePairs[editKey[0]].Count; i++)
                for (int j = 0; j < editKey.Length; j++)
                    result += keyValuePairs[editKey[j]][i].ToString();
            return result;
        }
        /// <summary>
        /// Получение словаря ключ-значение.
        /// </summary>
        /// <param name="message">Сообщение, которое нужно привязать к буквам ключа</param>
        /// <param name="key">Буквы ключа, к которым будет привязываться сообщение</param>
        /// <returns>Словарь</returns>
        private Dictionary<char, List<char>> GetDictionary(string message, string key)
        {
            Dictionary<char, List<char>> result = new Dictionary<char, List<char>>();
            
            for (int i = 0; i < key.Length; i++)
            {
                List<char> listChar = new List<char>();
                //добавление символов столбца ключу
                for (int j = i; j < message.Length; j += key.Length)
                {
                    listChar.Add(message[j]);
                }
                //связывание ключ-значение
                result.Add(key[i], listChar);
            }

            return result;
        }
    }
}

  • Вопрос задан
  • 3629 просмотров
Решения вопроса 1
lam0x86
@lam0x86
Честно говоря, я не совсем понял, в чём состоит проблема. Если файл не слишком большой, то можно прочитать его в память при помощи File.ReadAllText, затем закодировать содержимое и записать в другой файл. Если файл большой, то можно использовать FileStream - логика немного меняется, но тоже ничего сложного.

Я немного оптимизировал Ваш код для строк:
class Crypto2
    {
        private readonly int[] _key;
        private readonly int[] _inversedKey;

        public Crypto2(string key)
        {
            var indexPairs = key
                .Select((chr, idx1) => new { chr, idx1 }) // присваиваем каждому символу индекс
                .OrderBy(arg => arg.chr) // сортируем по символам
                .Select((arg, idx2) => new { arg.idx1, idx2 }) // и теперь создаём массив из пар "первоначальный индекс" <=> "отсортированный индекс".
                .ToArray();

            // Генерируем прямой ключ: чем "меньше" символ (в алфавитном порядке), тем меньше его индекс в конечном массиве.
            // "а" => 0, ... , "ь" => 5
            // Получаем: "цезарь" => [4, 1, 2, 0, 3, 5]
            _key = indexPairs
                .OrderBy(arg => arg.idx1)
                .Select(arg => arg.idx2)
                .ToArray();

            // Обратная операция для декодирования
            _inversedKey = indexPairs
                .OrderBy(arg => arg.idx2)
                .Select(arg => arg.idx1)
                .ToArray();
        }

        public string Encrypt(string message)
        {
            return EncryptDecrypt(message, _key);
        }

        public string Decrypt(string message)
        {
            return EncryptDecrypt(message, _inversedKey);
        }

        private static string EncryptDecrypt(string message, int[] key)
        {
            var keyLength = key.Length;
            var messageLength = message.Length;
            if (messageLength % keyLength > 0)
            {
                // Дополняем строку пробелами, если необходимо
                message = message.PadRight(messageLength + keyLength - messageLength % keyLength, ' ');
            }

            // Никогда не используйте конкатенацию строк в цикле (типа result += keyValuePairs[editKey[j]][i].ToString();). Это очень медленно.
            // Для этих целей есть специальный класс StringBuilder.
            // Мы заранее знаем длину конечной строки, поэтому инициализируем StringBuilder правильно.
            var sb = new StringBuilder(messageLength) {Length = message.Length};

            for (var i = 0; i < message.Length; i++)
            {
                // Вычисляем конечный индекс для вставки символа. 
                // Первая часть выражения "i / keyLength * keyLength" - округление i вниз до ближайшего значения, делящегося на keyLength
                // Вторая часть "key[i % keyLength]" - вычисление новой позиции символа на основе ключа.
                var idx = i / keyLength * keyLength + key[i % keyLength];
                sb[idx] = message[i];
            }

            return sb.ToString();
        }
    }


А вот тот же код для файлов:

class Crypto3
    {
        private readonly int[] _key;
        private readonly int[] _inversedKey;

        public Crypto3(string key)
        {
            var indexPairs = key
                .Select((chr, idx1) => new {chr, idx1})
                .OrderBy(arg => arg.chr)
                .Select((arg, idx2) =>
                    new
                    {
                        arg.idx1, idx2
                    })
                .ToArray();

            _key = indexPairs
                .OrderBy(arg => arg.idx1)
                .Select(arg => arg.idx2)
                .ToArray();

            _inversedKey = indexPairs
                .OrderBy(arg => arg.idx2)
                .Select(arg => arg.idx1)
                .ToArray();
        }

        public void Encrypt(string sourceFile, string destinationFile)
        {
            EncryptDecrypt(sourceFile, destinationFile, _key);
        }

        public void Decrypt(string sourceFile, string destinationFile)
        {
            EncryptDecrypt(sourceFile, destinationFile, _inversedKey);
        }

        private static void EncryptDecrypt(string sourceFile, string destinationFile, int[] key)
        {
            var keyLength = key.Length;
            var buffer1 = new byte[keyLength];
            var buffer2 = new byte[keyLength];
            using (var source = new FileStream(sourceFile, FileMode.Open))
            using (var destination = new FileStream(destinationFile, FileMode.OpenOrCreate))
            {
                while (true)
                {
                    var read = source.Read(buffer1, 0, keyLength);
                    if (read == 0)
                    {
                        return;
                    }
                    else if (read < keyLength)
                    {
                        for (int i = read; i < keyLength; i++)
                        {
                            buffer1[i] = (byte) ' ';
                        }
                    }

                    for (var i = 0; i < keyLength; i++)
                    {
                        var idx = i / keyLength * keyLength + key[i % keyLength];
                        buffer2[idx] = buffer1[i];
                    }

                    destination.Write(buffer2, 0, keyLength);
                }
            }
        }
    }
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы