С# Шифрование вывода текста

Я создал несколько небольших программ, которые экспортируют данные в текстовый файл с помощью StreamWriter, а затем считываю их с помощью StreamReader. Это прекрасно работает и делает то, что мне нужно, но мне было интересно, есть ли способ сохранить эту информацию, чтобы пользователь не мог получить к ней доступ или изменить ее намеренно или непреднамеренно. Примером того, что я хотел бы иметь в текстовом файле, было бы, если бы флажок был отмечен, когда вы отмечаете его, он выводит «Отмечено» в текстовый файл, когда программа повторно открывается, я знаю, в каком состоянии была форма, когда она был закрыт. Я, очевидно, не хочу продолжать использовать текстовые файлы. Есть ли у кого-нибудь идеи о том, как я могу легко хранить эту информацию, чтобы пользователь не мог ее изменить? Большое тебе спасибо.


person Bali C    schedule 22.08.2011    source источник


Ответы (9)


Самый простой способ — кодировать/декодировать этот текст в Base-64. Это небезопасно, но не позволит случайному пользователю изменить данные.

static public string EncodeTo64(string toEncode)
{
  byte[] toEncodeAsBytes
        = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
  string returnValue
        = System.Convert.ToBase64String(toEncodeAsBytes);
  return returnValue;
}

static public string DecodeFrom64(string encodedData)
{
  byte[] encodedDataAsBytes
      = System.Convert.FromBase64String(encodedData);
  string returnValue =
     System.Text.ASCIIEncoding.ASCII.GetString(encodedDataAsBytes);
  return returnValue;
}

EDIT: Настоящее шифрование

#region Encryption

        string passPhrase = "Pasword";        // can be any string
        string saltValue = "sALtValue";        // can be any string
        string hashAlgorithm = "SHA1";             // can be "MD5"
        int passwordIterations = 7;                  // can be any number
        string initVector = "~1B2c3D4e5F6g7H8"; // must be 16 bytes
        int keySize = 256;                // can be 192 or 128

        private string Encrypt(string data)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
            byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
            byte[] buffer = Encoding.UTF8.GetBytes(data);
            byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
            RijndaelManaged managed = new RijndaelManaged();
            managed.Mode = CipherMode.CBC;
            ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
            MemoryStream stream = new MemoryStream();
            CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write);
            stream2.Write(buffer, 0, buffer.Length);
            stream2.FlushFinalBlock();
            byte[] inArray = stream.ToArray();
            stream.Close();
            stream2.Close();
            return Convert.ToBase64String(inArray);
        }

        private string Decrypt(string data)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
            byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
            byte[] buffer = Convert.FromBase64String(data);
            byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
            RijndaelManaged managed = new RijndaelManaged();
            managed.Mode = CipherMode.CBC;
            ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
            MemoryStream stream = new MemoryStream(buffer);
            CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read);
            byte[] buffer5 = new byte[buffer.Length];
            int count = stream2.Read(buffer5, 0, buffer5.Length);
            stream.Close();
            stream2.Close();
            return Encoding.UTF8.GetString(buffer5, 0, count);
        }
        #endregion
person Leon    schedule 22.08.2011
comment
Спасибо. Я буду использовать для этого кодирование/декодирование Base-64, так как это звучит так, как будто это достигнет того, что мне нужно сделать. Могу ли я прочитать файл с этой статической общедоступной строкой DecodeFrom64(string pathoffile), используя путь к файлу в качестве аргумента, а не переменной? Я привык использовать StreamReader, поэтому задался вопросом, применимо ли это? - person Bali C; 22.08.2011
comment
Кроме того, если бы я использовал вышеупомянутый метод, где бы я указал местоположение файла и какое расширение я бы использовал? - person Bali C; 22.08.2011
comment
Вы можете изменить методы чтения из файла. Фактические методы, которые выполняют кодирование/декодирование для base64, работают с массивом байтов. Это всего лишь пример кода того, как это сделать из строки. В общем, я держу подобные вспомогательные методы в отдельном классе Utility, который затем можно вызывать из любого места программы. Вы можете использовать это: System.IO.StreamReader myFile = new System.IO.StreamReader(c:\\test.txt); строка myString = myFile.ReadToEnd(); и файл System.IO.StreamWriter = новый System.IO.StreamWriter(c:\\test.txt); файл.WriteLine(строки); файл.Закрыть(); - person Leon; 22.08.2011
comment
Бали, как упоминал @Leon, кодировка base-64 небезопасна. Если вы используете один из методов, упомянутых в этом посте, используйте шифрование. - person Michael; 22.08.2011
comment
Я знаю, что это устарело, но ваш метод Encrypt() постоянно выдает мне сообщение об ошибке byte[] rgbKey о том, что ссылка на объект не установлена ​​на экземпляр объекта. - person Shyy Guy; 03.05.2015
comment
Я проголосовал против, потому что использование строки Base64 почти удвоит размер файла. Этот ответ не является правильным решением. - person Krythic; 20.07.2015

Вы можете добавить контрольную сумму или хэш к файлу — если содержимое файла не соответствует контрольной сумме, вы знаете, что оно было подделано.

Если важно, чтобы пользователи не могли прочитать содержимое файла, вы можете зашифровать его.

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

person Oded    schedule 22.08.2011
comment
Вы даже не можете обнаружить такое вмешательство. Пользователь может добавить ту же контрольную сумму. - person SLaks; 22.08.2011
comment
Спасибо за предложения, мой приоритет в том, что данные не хранятся в виде простого текста, например, когда вы открываете DLL или EXE в блокноте, все они зашифрованы и не читаются (я знаю, что это из-за того, как это выполняется, но вы поняли, что я имею в виду - просто чтобы у пользователя не было соблазна что-то сломать только потому, что он может это прочитать) спасибо. - person Bali C; 22.08.2011
comment
Если бы я зашифровал текст, он бы не отображался в виде простого текста или был бы недоступен? Я так понимаю, приложение все равно сможет это прочитать? У вас есть пример кода, чтобы показать мне, как что-то зашифровать? Большое спасибо - person Bali C; 22.08.2011
comment
@Bali C - шифрование означает, что его нельзя прочитать без предварительной расшифровки. Я предлагаю вам просмотреть страницы MSDN для System.Security. Криптография. Тема большая и любой простой пример будет вводить в заблуждение. - person Oded; 22.08.2011

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

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

person SLaks    schedule 22.08.2011

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

person mtijn    schedule 22.08.2011
comment
Потому что пользователь по-прежнему может открыть его с помощью любой ZIP-программы. - person SLaks; 22.08.2011
comment
нет, если на нем есть приличный пароль, и выбор расширения файла, отличного от zip, не позволяет некоторым обычным пользователям пытаться - person mtijn; 22.08.2011
comment
Правда, хотя пользователь может декомпилировать программу и найти пароль. - person SLaks; 22.08.2011
comment
но это совсем другая проблема.. это зависит от того, насколько хорошо вы защищаете пароль в своем собственном приложении или где бы он ни хранился. - person mtijn; 22.08.2011
comment
в любом случае, если пользователь начинает декомпилировать программу для поиска паролей, он, вероятно, является опытным пользователем. вопрос не касался уровня требуемой защиты или экспертного уровня пользователей, поэтому, возможно, даже не нужно принимать во внимание этот вид защиты. - person mtijn; 22.08.2011

Если программа может получить доступ к информации, пользователь, как правило, тоже может. Однако вы можете создавать данные, которые пользователь не сразу поймет.

Я бы начал с создания класса, который содержит всю информацию о состоянии, которую вы хотите сохранить, изолируя проблему. По совпадению класс BinaryFormatter позволит вам легко сохранять и загружать этот класс в/из файла. Я не знаю, являются ли его результаты «достаточно нечитаемыми» - если нет, примените кодировку Base64, как упоминал Леон.

person C.Evenhuis    schedule 22.08.2011

Хотя вы можете кодировать base64 или даже полностью шифровать свои данные конфигурации (с помощью SHA1 или MD5), как уже предлагалось, я думаю, что хорошей практикой будет работать с классами фреймворка, имеющими дело с данными конфигурации (Configuration в пространстве имен System.Configuration) и имеет встроенную возможность шифрования данных (через метод ProtectSection в < класс href="http://msdn.microsoft.com/en-us/library/system.configuration.configurationsection.aspx" rel="nofollow">ConfigurationSection).

Прежде всего, вы должны объявить и инициализировать экземпляр:

using System.Configuration;
...
static void Main(string[] args)
    {
        Configuration config;

        config = ConfigurationManager.OpenExeConfiguration(/*path to config file*/); //Use ConfigurationManager.OpenMachineConfiguration(/*path to config file*/) when opening machine configuration
...

После этого вам необходимо определить раздел пользовательской конфигурации, который определяет вашу конфигурацию (пример MSDN)

После того, как вы это сделаете, вам просто нужно инициализировать экземпляр вашего пользовательского раздела конфигурации и добавить его в файл конфигурации, используя этот код:

isTicked = config.Sections.Add("isTicked", customSection);

Чтобы зашифровать только что добавленный раздел, используйте этот код (с дополнительными примерами как на VB.NET, так и на C#, найденными здесь):

config.Sections["isTicked"].SectionInformation.ProtectSection("protection provider");

«DPAPIProtectedConfigurationProvider» и «RSAProtectedConfigurationProvider» встроены по умолчанию.

Если вы хотите расшифровать раздел, используйте этот код:

config.Sections["isTicked"].SectionInformation.UnprotectSection();

Подчеркнем: и шифрование, и расшифровка вступают в силу только после сохранения файла конфигурации.

Чтобы сохранить файл, используйте код:

config.Save(); //config.SaveAs("string") is also available

Дополнительную информацию о соответствующих классах и методах можно найти в msdn, начиная со страницы класса Configuration, указанной выше.

person Michael    schedule 22.08.2011

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

public static class Crypto
{
    private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };


    public static string Encrypt(this string text, string salt)
    {
        try
        {
            using (Aes aes = new AesManaged())
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                aes.IV = aes.Key;
                using (MemoryStream encryptionStream = new MemoryStream())
                {
                    using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        byte[] cleanText = Encoding.UTF8.GetBytes(text);
                        System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption text data size: ", text.Length.ToString()));
                        System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption byte data size: ", cleanText.Length.ToString()));
                        encrypt.Write(cleanText, 0, cleanText.Length);
                        encrypt.FlushFinalBlock();
                    }

                    byte[] encryptedData = encryptionStream.ToArray();
                    string encryptedText = Convert.ToBase64String(encryptedData);

                    System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", encryptedText.Length.ToString()));
                    System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));

                    return encryptedText;
                }
            }
        }
        catch(Exception e)
        {
            return String.Empty;
        }
    }

    public static string Decrypt(this string text, string salt)
    {
        try
        {
            using (Aes aes = new AesManaged())
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                aes.IV = aes.Key;

                using (MemoryStream decryptionStream = new MemoryStream())
                {
                    using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        byte[] encryptedData = Convert.FromBase64String(text);

                        System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", text.Length.ToString()));
                        System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));

                        decrypt.Write(encryptedData, 0, encryptedData.Length);
                        decrypt.Flush();
                    }

                    byte[] decryptedData = decryptionStream.ToArray();
                    string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);

                    System.Diagnostics.Debug.WriteLine(String.Concat("After decryption text data size: ", decryptedText.Length.ToString()));
                    System.Diagnostics.Debug.WriteLine(String.Concat("After decryption byte data size: ", decryptedData.Length.ToString()));

                    return decryptedText;
                }
            }
        }
        catch(Exception e)
        {
            return String.Empty;
        }
    }
}
person Community    schedule 24.08.2012
comment
хороший код, печально, что нужно жестко кодировать IVa, так как это сделало бы его обратимым, декомпилировать код, и ключ есть. - person Walter Vehoeven; 28.11.2018

Просто добавьте еще одну реализацию ответа Леона и следуйте документы Microsoft

Вот пример класса, который шифрует и расшифровывает строки

    public static class EncryptionExample
{

    #region internal consts

    internal const string passPhrase = "pass";
    internal const string saltValue = "salt";
    internal const string hashAlgorithm = "MD5";
    internal const int passwordIterations = 3;             // can be any number
    internal const string initVector = "0123456789abcdf"; // must be 16 bytes
    internal const int keySize = 64;                      // can be 192 or 256

    #endregion

    #region public static Methods

    public static string Encrypt(string data)
    {
        string res = string.Empty;
        try
        {
            byte[] bytes = Encoding.ASCII.GetBytes(initVector);
            byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
            byte[] buffer = Encoding.UTF8.GetBytes(data);
            byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
            RijndaelManaged managed = new RijndaelManaged();
            managed.Mode = CipherMode.CBC;
            ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);

            byte[] inArray = null;
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(buffer, 0, buffer.Length);
                    csEncrypt.FlushFinalBlock();
                    inArray = msEncrypt.ToArray();
                    res = Convert.ToBase64String(inArray);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Encrypt " + ex);
        }

        return res;
    }

    public static string Decrypt(string data)
    {
        string res = string.Empty;
        try
        {
            byte[] bytes = Encoding.ASCII.GetBytes(initVector);
            byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
            byte[] buffer = Convert.FromBase64String(data);
            byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
            RijndaelManaged managed = new RijndaelManaged();
            managed.Mode = CipherMode.CBC;
            ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);

            using (MemoryStream msEncrypt = new MemoryStream(buffer))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        res = srDecrypt.ReadToEnd();
                    }
                }
            }
        }
        catch (Exception ex)
        {
           Console.WriteLine("Decrypt " + ex);
        }

        return res;
    }
}

Кстати, вот определение «солевой ценности», которое я погуглил, чтобы узнать, что это такое.

Соленое значение

Если злоумышленник не знает пароль и пытается угадать его с помощью грубой силы, то каждый пароль, который он пробует, должен быть проверен с каждым значением соли. Таким образом, для однобитовой соли (0 или 1) шифрование становится вдвое труднее взломать таким образом.

person Gonzalo Fernández    schedule 27.10.2015

Предотвратить непреднамеренное изменение строки можно с помощью контрольной суммы, как указано в этом ответе.

Однако сгенерировать такую ​​контрольную сумму довольно просто, так как они не так уж и много широко используемых алгоритмов .

Таким образом, это не защищает вас от намеренного изменения.

Чтобы предотвратить это, люди используют цифровые подписи. Это позволяет любому убедиться, что ваши данные не были подделаны, но только вы (владелец личного секрета) можете создать подпись.

Вот пример на C#.

Однако, как указывали другие, вам нужно внедрить свой закрытый ключ где-нибудь в свой двоичный файл, и (не очень) опытный программист сможет его получить, даже если вы запутаете свою .net dll или сделаете это в отдельном нативном процесс.

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

Если вас действительно заботит безопасность, вам нужно перейти в облако и выполнить код на своей машине.

person Regis Portalez    schedule 24.07.2018