Как перекодировать функции для использования строк вместо FileStreams - PullRequest
1 голос
/ 07 мая 2019

Я создаю игру, которая требует зашифрованных сохранений и т. Д. Поэтому я делаю несколько функций, чтобы помочь мне зашифровать их. Тем не менее, единственная функция, которая у меня есть на данный момент, использует FileStreams для ввода и вывода, но я бы хотел использовать вместо нее строки. Функции прекрасно работают с файлами, но у меня возникают проблемы при переключении файлов с FileStreams на MemoryStreams на строки. ПРИМЕЧАНИЕ. УДАЛЕН НЕОБХОДИМЫЙ КОД. OurCodeWorld.GenerateRandomSalt() 100% работает, протестировано с шифрованием FileStream

ПОЛНЫЙ КОД:

Program.cs:

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;


namespace Encryption_test
{

    class Program
    {
        static public string encryptedExtension = ".aes";
        static public string decryptedExtension = ".decrypted";

        static void Main(string[] args)
        {
            string path = Environment.CurrentDirectory;
            Log($"Current path: {path}");
            string filePath = $"{path}/unencrypted.txt";
            string message =
@"Hello world!
This is my message. 1234";

            RunStackOverFlowString();

            //Sleep forever...zzz
            Thread.Sleep(10000);
            Console.WriteLine();

            float TicksToMs(long ticks)
            {
                return ticks / (float)Stopwatch.Frequency * 1000F;
            }

            void RunStackOverFlowString()
            {
                byte[] salt = OurCodeWorld.GenerateRandomSalt();
                int iterations = 1024;
                string password = "";
                string enc = StackOverflow.EncryptString(message, password, salt, iterations);
                Log($"Enc: {enc}");
                string dec = StackOverflow.DecryptString(enc, password, salt, iterations);
                Log($"Dec: {dec}");
            }
        private static void WriteFile(string path, string value)
        {
            FileStream _file = File.OpenWrite(path);
            byte[] info = new UTF8Encoding(true).GetBytes(value);
            _file.Write(info, 0, info.Length);
            _file.Close();
        }

        private static string ReadFile(string filePath, long length = long.MaxValue)
        {
            FileStream _file = File.OpenRead(filePath);
            if (length == long.MaxValue)
                length = _file.Length;

            byte[] b = new byte[length];
            UTF8Encoding temp = new UTF8Encoding(true);

            _file.Read(b, 0, b.Length);
            _file.Close();

            return temp.GetString(b);
        }

        private static void DeleteFile(string path)
        {
            File.Delete(path);
        }
        private static void CreateFile(string path)
        {
            if (File.Exists(path))
                DeleteFile(path);
            FileStream a = File.Open(path, FileMode.Create);
            a.Close();
        }
        static void Log(string message)
        {
            Console.WriteLine(message);
        }
    }
}

StackOverFlow.cs:

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

class StackOverflow
{

    // Rfc2898DeriveBytes constants:

    /// <summary>Decrypt a file.</summary>
    /// <remarks>NB: "Padding is invalid and cannot be removed." is the Universal CryptoServices error.  Make sure the password, salt and iterations are correct before getting nervous.</remarks>
    /// <param name="sourceFilename">The full path and name of the file to be decrypted.</param>
    /// <param name="destinationFilename">The full path and name of the file to be output.</param>
    /// <param name="password">The password for the decryption.</param>
    /// <param name="salt">The salt to be applied to the password.</param>
    /// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
    public static void DecryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
    {
        AesManaged aes = new AesManaged();
        aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
        aes.KeySize = aes.LegalKeySizes[0].MaxSize;
        // NB: Rfc2898DeriveBytes initialization and subsequent calls to   GetBytes   must be eactly the same, including order, on both the encryption and decryption sides.
        Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
        aes.Key = key.GetBytes(aes.KeySize / 8);
        aes.IV = key.GetBytes(aes.BlockSize / 8);
        aes.Mode = CipherMode.CBC;
        ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);

        using (FileStream destination = new FileStream(destinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
            {
                try
                {
                    using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        source.CopyTo(cryptoStream);
                    }
                }
                catch (CryptographicException exception)
                {
                    if (exception.Message == "Padding is invalid and cannot be removed.")
                        throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
                    else
                        throw;
                }
            }
        }
    }

    /// <summary>Encrypt a file.</summary>
    /// <param name="sourceFilename">The full path and name of the file to be encrypted.</param>
    /// <param name="destinationFilename">The full path and name of the file to be output.</param>
    /// <param name="password">The password for the encryption.</param>
    /// <param name="salt">The salt to be applied to the password.</param>
    /// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
    public static void EncryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
    {
        AesManaged aes = new AesManaged();
        aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
        aes.KeySize = aes.LegalKeySizes[0].MaxSize;
        // NB: Rfc2898DeriveBytes initialization and subsequent calls to   GetBytes   must be eactly the same, including order, on both the encryption and decryption sides.
        Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
        aes.Key = key.GetBytes(aes.KeySize / 8);
        aes.IV = key.GetBytes(aes.BlockSize / 8);
        aes.Mode = CipherMode.CBC;
        ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);

        using (FileStream destination = new FileStream(destinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
            {
                using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    source.CopyTo(cryptoStream);
                }
            }
        }
    }

    //THIS IS MY CODE
    public static string EncryptString(string inputString, string password, byte[] salt, int iterations)
    {
        AesManaged aes = new AesManaged();
        aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
        aes.KeySize = aes.LegalKeySizes[0].MaxSize;
        // NB: Rfc2898DeriveBytes initialization and subsequent calls to   GetBytes   must be eactly the same, including order, on both the encryption and decryption sides.
        Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
        aes.Key = key.GetBytes(aes.KeySize / 8);
        aes.IV = key.GetBytes(aes.BlockSize / 8);
        aes.Mode = CipherMode.CBC;
        ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);

        var source = StringToStream(inputString);
        var output = new MemoryStream();

        CryptoStream cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write);
        source.CopyTo(cryptoStream);
        return StreamToString(output);
    }

    public static string DecryptString(string inputString, string password, byte[] salt, int iterations)
    {
        AesManaged aes = new AesManaged();
        aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
        aes.KeySize = aes.LegalKeySizes[0].MaxSize;
        // NB: Rfc2898DeriveBytes initialization and subsequent calls to   GetBytes   must be eactly the same, including order, on both the encryption and decryption sides.
        Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
        aes.Key = key.GetBytes(aes.KeySize / 8);
        aes.IV = key.GetBytes(aes.BlockSize / 8);
        aes.Mode = CipherMode.CBC;
        ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);

        var source = StringToStream(inputString);
        var output = new MemoryStream();

        CryptoStream cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write);
        source.CopyTo(cryptoStream);
        return StreamToString(output);
    }


    public static Stream StringToStream(string s)
    {
        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);
        writer.Write(s);
        writer.Flush();
        stream.Position = 0;
        return stream;
    }
    public static string StreamToString(Stream s)
    {
        s.Position = 0;
        byte[] buffer = new byte[s.Length];
        s.Read(buffer, 0, (int)s.Length);
        return Encoding.Default.GetString(buffer);
    }
}

Я уже тестировал методы StreamToString и StringToStream, и они работают нормально. Когда я запускаю DecryptString, ничего не выводится, и функция возвращает искаженные строки, часто похожие на эту Dec: ?K??? ?@?????n?l?r????T?

Ответы [ 2 ]

0 голосов
/ 07 мая 2019

Я проверял код.Есть две проблемы:

  1. Вы берете зашифрованные байты и конвертируете их в строку, используя кодировку.Этот процесс в целом завершится неудачно, потому что вы не можете взять произвольные байты и преобразовать их в строку.Соотношение между байтами и строками не 1: 1.Скорее всего, здесь вам вообще не следует работать со строками.Скорее сохраните данные в байтовом формате и запишите эти байты в файл.Подпись метода должна быть как byte[] Encrypt(byte[] input, ...).Код шифрования не имеет смысла преобразовывать строки в строки.
  2. EncryptString должен очищать криптопоток, используя FlushFinalBlock.Если этого не сделать, данные в конце будут отсутствовать.
0 голосов
/ 07 мая 2019

Преобразование между строками и двоичными данными выполняется с помощью Encoding. Кодировка определяет, как символы будут преобразованы в двоичные данные. Распространенными кодировками являются Unicode, UTF8 или ASCII. Если вы хотите получить предсказуемые результаты, вам нужно использовать определенную кодировку для обоих преобразований.

В вашем StringToStream вы вообще не указываете свою кодировку, в то время как вы используете Encoding.Default в StreamToString. Encoding.Default не обеспечивает предсказуемый результат, поскольку использует текущую кодовую страницу (если вы работаете в системе Windows). Так что вы не можете быть уверены, что вы используете одну и ту же кодировку в обоих методах.

Решение состоит в том, чтобы обеспечить одинаковую кодировку на обоих концах:

public static Stream StringToStream(string s)
{
    byte[] buffer = Encoding.UTF8.GetBytes(s);
    return new MemoryStream(buffer);
}

public static string StreamToString(Stream s)
{
    s.Position = 0;
    byte[] buffer = new byte[s.Length];
    s.Read(buffer, 0, (int)s.Length);
    return Encoding.UTF8.GetString(buffer);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...