Зашифровать и расшифровать строку на C#?

Как я могу зашифровать и расшифровать строку на C#?

Требовалось что-то простое ... у меня работала эта ссылка saipanyam.net/2010/03/encrypt-query-strings.html

MrM 19.04.2011 00:31

Проверить эту ссылку codeproject.com/KB/recipes/Encrypt_an_string.aspx

DmitryBoyko 17.12.2010 22:28

Я НАСТОЯТЕЛЬНО рекомендую отказаться от 3DES и использовать AES-GCM. AES-GCM НЕТ в криптографических библиотеках .NET 4.5 и отличается от «обычного AES» (= обычно режим AES-CBC). AES-GCM намного лучше, чем «обычный» AES, по криптографическим причинам, в которые я не буду вдаваться. Итак, у jbtule есть лучший ответ ниже в этом подразделе Bouncy Castle AES-GCM. Если вы нам не верите, по крайней мере, доверяйте экспертам из АНБ (NSA Suite B @ nsa.gov/ia/programs/suiteb_cryptography/index.shtml: The Galois/Counter Mode (GCM) is the preferred AES mode.)

DeepSpace101 25.01.2013 13:45

@Sid Лично я предпочел бы AES-CBC + HMAC-SHA2, а не AES-GCM для большинства ситуаций. GCM катастрофически терпит неудачу, если вы когда-либо повторно используете одноразовый номер.

CodesInChaos 28.02.2013 00:08

Повторное использование @Sid Nonce - плохая идея, да. Но я видел, как это происходило даже с грамотными программистами / криптографами. Если это произойдет, GCM полностью выйдет из строя, тогда как CBC + HMAC обнаружит лишь некоторые незначительные недостатки. С протоколом, подобным SSL, GCM подходит, но мне не нравится использовать его как стандартный API «шифрования и аутентификации».

CodesInChaos 28.02.2013 01:54

Возможный дубликат Простое двустороннее шифрование для C#

angularsen 23.10.2014 02:57

Возможный дубликат Шифрование и дешифрование строки в C#

CraigTP 23.12.2015 14:38

Как насчет использования инструкций AES INTEL-NL для шифрования и дешифрования, то есть AESKEYGENASSIST, AESIMC, AESENC, AESENCLAST, AESDEC, AESDECLAST будет работать намного быстрее, не так ли? т как нижний регистр.

user80998 08.11.2020 21:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
718
8
719 752
29

Ответы 29

Вот пример использования RSA.

Важный: Существует ограничение на размер данных, которые вы можете зашифровать с помощью шифрования RSA, KeySize - MinimumPadding. например 256 байтов (при условии 2048-битного ключа) - 42 байта (минимальное заполнение OEAP) = 214 байта (максимальный размер открытого текста)

Замените your_rsa_key своим ключом RSA.

var provider = new System.Security.Cryptography.RSACryptoServiceProvider();
provider.ImportParameters(your_rsa_key);

var encryptedBytes = provider.Encrypt(
    System.Text.Encoding.UTF8.GetBytes("Hello World!"), true);

string decryptedTest = System.Text.Encoding.UTF8.GetString(
    provider.Decrypt(encryptedBytes, true));

Для получения дополнительной информации посетите MSDN - RSACryptoServiceProvider

Извините за такой простой вопрос, но может ли кто-нибудь сказать мне, где я могу получить ключ RSA или как его сгенерировать?

Akash Kava 05.11.2009 20:36

Почему RSA? У RSA есть свои применения, но ничто не указывает на то, что это одно из них.

CodesInChaos 17.07.2011 17:35

@CodeInChaos: потому что, когда я дал этот ответ почти 3 года назад, вопрос изначально ничего не указывал. Это было добавлено позже. Я ответил на исходный вопрос. Посмотрите историю вопросов и убедитесь в этом сами.

Tamas Czinege 27.07.2011 03:35

Даже в исходном вопросе нет никаких указаний на то, что RSA может подойти. Асимметричное шифрование имеет свои применения, но это неправильный выбор в качестве шифрования по умолчанию. Ваш примерный код не будет работать с более длинными строками, потому что класс RSA не предназначен для шифрования общего назначения. Если вам нужны асимметричные функции, вам следует зашифровать симметричный ключ с помощью RSA и зашифровать фактические данные с помощью этого симметричного ключа. Так что я все еще считаю ваш ответ плохим советом.

CodesInChaos 27.07.2011 13:08

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

Otto Kanellis 16.02.2014 12:46

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

Tomer W 22.02.2015 15:32

РЕДАКТИРОВАТЬ 2013-октябрь: Хотя я со временем редактировал этот ответ, чтобы устранить недостатки, см. ответ jbtule для более надежного и информированного решения.

https://stackoverflow.com/a/10366194/188474

Оригинальный ответ:

Вот рабочий пример, полученный из Документация "RijndaelManaged Class" и Учебный комплект MCTS.

РЕДАКТИРОВАТЬ 2012-апрель: этот ответ был отредактирован, чтобы предварительно отложить IV для предложения jbtule и, как показано здесь:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.95%29.aspx

Удачи!

public class Crypto
{

    //While an app specific salt is not the best practice for
    //password based encryption, it's probably safe enough as long as
    //it is truly uncommon. Also too much work to alter this answer otherwise.
    private static byte[] _salt = __To_Do__("Add a app specific salt here");

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match.
    /// </summary>
    /// <param name = "plainText">The text to encrypt.</param>
    /// <param name = "sharedSecret">A password used to generate a key for encryption.</param>
    public static string EncryptStringAES(string plainText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        string outStr = null;                       // Encrypted string to return
        RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create a RijndaelManaged object
            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            // Create a decryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // prepend the IV
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                }
                outStr = Convert.ToBase64String(msEncrypt.ToArray());
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    /// <param name = "cipherText">The text to decrypt.</param>
    /// <param name = "sharedSecret">A password used to generate a key for decryption.</param>
    public static string DecryptStringAES(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create the streams used for decryption.                
            byte[] bytes = Convert.FromBase64String(cipherText);
            using (MemoryStream msDecrypt = new MemoryStream(bytes))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }
}

Брету - спасибо за ваш пример. Можно подумать - у меня была проблема с длиной ключа - я сделал модификацию с помощью MD5, поэтому, если кто-то будет использовать ваш пример в функции, пожалуйста, используйте его для нормализации ключа (или вы можете использовать другой алгоритм хеширования: HashAlgorithm hash = new MD5CryptoServiceProvider (); UnicodeEncoding UE = new UnicodeEncoding (); byte [] key = hash.ComputeHash (UE.GetBytes (encrypt_password)); ps: извините за мой английский :) slinti

user593912 29.01.2011 19:19

какова длина массива байтов результата (как функция длины ввода plainText)? Спасибо!

nakhli 19.12.2011 00:17

Размер блока 16 байт. Длина выходного байта будет того же размера, что и входного, с округлением в большую сторону до заполнения блока. например, входная строка из 120 байтов приведет к выходной строке из 8 блоков или 128 байтов. См .: en.wikipedia.org/wiki/Block_size_%28cryptography%29

Brett 19.12.2011 05:43

Похоже, что AES - это более новая форма Rijndeal, и согласно MSDN AES следует использовать вместо Rijndeal: так что используйте AesManaged вместо этого? Больше информации здесь

John Bubriski 28.01.2012 01:36

Полагаю, AES недоступен в версии 2.0.

John Bubriski 31.01.2012 02:02

Приведенный выше код небезопасен, он нарушает самое основное правило семантической безопасности с помощью aes, вам НИКОГДА не следует использовать один и тот же IV более одного раза с одним и тем же ключом. Это всегда дает идентичный IV каждый раз, когда вы используете один и тот же ключ.

jbtule 25.04.2012 02:54

@jbtule - Спасибо. Пожалуйста, смотрите мои правки выше. Я считаю, что получение ключа из пароля удовлетворит ваши опасения. В тексте MCTS о IV говорится следующее: «Как и свойство Key, и шифровальщик, и дешифратор должны указывать одно и то же значение. Чтобы избежать накладных расходов на безопасную передачу IV между шифровальщиком и дешифратором, вы можете определить IV в своем статически или получить это из свойства Key. " Приведенный выше код делает последнее - извлекает IV из ключа.

Brett 27.04.2012 06:03

Это текущий текст MCTS ?! Избежать всего лишь одного блока накладных расходов за счет отключения функции безопасности AES не похоже на то, что Microsoft может обучать людей делать. Коррект как минимум микрософц документы для AES.

jbtule 27.04.2012 06:40

Это хороший ответ как использовать капельницу, что текст MCTS безответственный и нестандартный.

jbtule 27.04.2012 17:48

Использование соли в процессе получения ключа не повредит. Константа - плохая соль, так же как константа - плохой IV.

CodesInChaos 14.08.2013 19:48

Что касается путаницы между AES и Rijndael: AES - это подмножество Rijndael. Если вы используете Rijndael со 128-битными блоками и 128-, 192- или 256-битными ключами, вы используете AES.

CodesInChaos 14.08.2013 19:50

Итак, _salt - это ключ, который вы хотите сохранить правильно?

Rod 06.10.2014 22:45

Соль добавляет степень обфускации, чтобы предотвратить растрескивание. Рекомендую вам прочитать ниже примеры jbtules, где генерируется соль.

Brett 07.10.2014 00:16

RijndaelManaged имеет метод GenerateIV()

Ian Warburton 18.12.2015 17:26

@IanWarburton Мы просто ссылаемся на свойство IV класса RijndaelManaged. В документации указано, что IV устанавливается на новое случайное значение всякий раз, когда вы создаете экземпляр класса. См. Раздел примечаний в документации: bit.ly/1ObZRzg

Brett 18.12.2015 17:40

Разве не уязвимость заключается в том, что вы транспортируете IV в незашифрованном виде? Разве это не теряет своего смысла, если злоумышленник знает IV?

Foxman 27.03.2016 01:38

Для расшифровки требуются IV и ключ. Случайный IV обеспечивает «семантическую безопасность» - не позволяет любому, кто наблюдает за трафиком, различать шаблоны. Например, если бы для всех паролей было установлено значение «изменить меня», случайные IV все же привели бы к случайному зашифрованному тексту, даже если бы ключ был тем же самым.

Brett 27.03.2016 04:07

RijndaelManaged не соответствует требованиям FIPS 140-2 и может вызывать исключение в системах DOD, где включен FIPS.

Farukh 26.07.2016 02:00

using System;
using System.Data;
using System.Configuration;
using System.Text;
using System.Security.Cryptography;

namespace Encription
{
    class CryptorEngine
    {
        public static string Encrypt(string ToEncrypt, bool useHasing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(ToEncrypt);
            //System.Configuration.AppSettingsReader settingsReader = new     AppSettingsReader();
           string Key = "Bhagwati";
            if (useHasing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(Key));
                hashmd5.Clear();  
            }
            else
            {
                keyArray = UTF8Encoding.UTF8.GetBytes(Key);
            }
            TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();
            tDes.Key = keyArray;
            tDes.Mode = CipherMode.ECB;
            tDes.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tDes.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,     toEncryptArray.Length);
            tDes.Clear();
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        public static string Decrypt(string cypherString, bool useHasing)
        {
            byte[] keyArray;
            byte[] toDecryptArray = Convert.FromBase64String(cypherString);
            //byte[] toEncryptArray = Convert.FromBase64String(cypherString);
            //System.Configuration.AppSettingsReader settingReader = new     AppSettingsReader();
            string key = "Bhagwati";
            if (useHasing)
            {
                MD5CryptoServiceProvider hashmd = new MD5CryptoServiceProvider();
                keyArray = hashmd.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hashmd.Clear();
            }
            else
            {
                keyArray = UTF8Encoding.UTF8.GetBytes(key);
            }
            TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();
            tDes.Key = keyArray;
            tDes.Mode = CipherMode.ECB;
            tDes.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tDes.CreateDecryptor();
            try
            {
                byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0,         toDecryptArray.Length);

                tDes.Clear();
                return UTF8Encoding.UTF8.GetString(resultArray,0,resultArray.Length);
            }
            catch (Exception ex)
            {
                throw ex;
             }
        }
    }
}

Разве режим шифрования ECB - не большой запрет?

John Bubriski 28.01.2012 00:14

Да, ECB - наименее безопасный вариант. См. Комментарии MS: «Важно: этот режим не рекомендуется, потому что он открывает дверь для множества уязвимостей безопасности». msdn.microsoft.com/en-us/library/…

Rich 05.12.2012 14:34

Современные примеры симметричного аутентифицированного шифрования строки.

Общая передовая практика для симметричного шифрования - использовать аутентифицированное шифрование со связанными данными (AEAD), однако это не является частью стандартных криптографических библиотек .net. Итак, в первом примере используется AES256, а затем HMAC256, двухэтапный Зашифруйте затем MAC, который требует больше накладных расходов и дополнительных ключей.

Во втором примере используется более простая практика AES256-GCM с использованием Bouncy Castle с открытым исходным кодом (через nuget).

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

В обоих примерах также есть вспомогательные методы, которые используют строковый пароль для генерации ключей. Эти вспомогательные методы предоставляются для удобства, чтобы сопоставить их с другими примерами, однако это гораздо менее безопасный, потому что надежность пароля будет намного слабее 256-битного ключа.

Обновлять: Добавлены перегрузки byte[], и только Суть имеет полное форматирование с отступом в 4 пробела и документами api из-за ограничений ответа StackOverflow.


Встроенное шифрование .NET (AES) - затем MAC (HMAC) [Суть]

/*
 * This work (Modern Encryption of a String C#, by James Tuley), 
 * identified by James Tuley, is free of known copyright restrictions.
 * https://gist.github.com/4336842
 * http://creativecommons.org/publicdomain/mark/1.0/ 
 */

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

namespace Encryption
{
  public static class AESThenHMAC
  {
    private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();

    //Preconfigured Encryption Parameters
    public static readonly int BlockBitSize = 128;
    public static readonly int KeyBitSize = 256;

    //Preconfigured Password Key Derivation Parameters
    public static readonly int SaltBitSize = 64;
    public static readonly int Iterations = 10000;
    public static readonly int MinPasswordLength = 12;

    /// <summary>
    /// Helper that generates a random key on each call.
    /// </summary>
    /// <returns></returns>
    public static byte[] NewKey()
    {
      var key = new byte[KeyBitSize / 8];
      Random.GetBytes(key);
      return key;
    }

    /// <summary>
    /// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message.
    /// </summary>
    /// <param name = "secretMessage">The secret message.</param>
    /// <param name = "cryptKey">The crypt key.</param>
    /// <param name = "authKey">The auth key.</param>
    /// <param name = "nonSecretPayload">(Optional) Non-Secret Payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">Secret Message Required!;secretMessage</exception>
    /// <remarks>
    /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize +  HMac-Tag(32)) * 1.33 Base64
    /// </remarks>
    public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey,
                       byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }

    /// <summary>
    /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message.
    /// </summary>
    /// <param name = "encryptedMessage">The encrypted message.</param>
    /// <param name = "cryptKey">The crypt key.</param>
    /// <param name = "authKey">The auth key.</param>
    /// <param name = "nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey,
                       int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    /// <summary>
    /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message
    /// using Keys derived from a Password (PBKDF2).
    /// </summary>
    /// <param name = "secretMessage">The secret message.</param>
    /// <param name = "password">The password.</param>
    /// <param name = "nonSecretPayload">The non secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">password</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// Adds additional non secret payload for key generation parameters.
    /// </remarks>
    public static string SimpleEncryptWithPassword(string secretMessage, string password,
                             byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }

    /// <summary>
    /// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message
    /// using keys derived from a password (PBKDF2). 
    /// </summary>
    /// <param name = "encryptedMessage">The encrypted message.</param>
    /// <param name = "password">The password.</param>
    /// <param name = "nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// </remarks>
    public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
                             int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
    {
      //User Error Checks
      if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");

      if (authKey == null || authKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");

      if (secretMessage == null || secretMessage.Length < 1)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      //non-secret payload optional
      nonSecretPayload = nonSecretPayload ?? new byte[] { };

      byte[] cipherText;
      byte[] iv;

      using (var aes = new AesManaged
      {
        KeySize = KeyBitSize,
        BlockSize = BlockBitSize,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7
      })
      {

        //Use random IV
        aes.GenerateIV();
        iv = aes.IV;

        using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
        using (var cipherStream = new MemoryStream())
        {
          using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
          using (var binaryWriter = new BinaryWriter(cryptoStream))
          {
            //Encrypt Data
            binaryWriter.Write(secretMessage);
          }

          cipherText = cipherStream.ToArray();
        }

      }

      //Assemble encrypted message and add authentication
      using (var hmac = new HMACSHA256(authKey))
      using (var encryptedStream = new MemoryStream())
      {
        using (var binaryWriter = new BinaryWriter(encryptedStream))
        {
          //Prepend non-secret payload if any
          binaryWriter.Write(nonSecretPayload);
          //Prepend IV
          binaryWriter.Write(iv);
          //Write Ciphertext
          binaryWriter.Write(cipherText);
          binaryWriter.Flush();

          //Authenticate all data
          var tag = hmac.ComputeHash(encryptedStream.ToArray());
          //Postpend tag
          binaryWriter.Write(tag);
        }
        return encryptedStream.ToArray();
      }

    }

    public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
    {

      //Basic Usage Error Checks
      if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize), "cryptKey");

      if (authKey == null || authKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize), "authKey");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      using (var hmac = new HMACSHA256(authKey))
      {
        var sentTag = new byte[hmac.HashSize / 8];
        //Calculate Tag
        var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length);
        var ivLength = (BlockBitSize / 8);

        //if message length is to small just return null
        if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength)
          return null;

        //Grab Sent Tag
        Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length);

        //Compare Tag with constant time comparison
        var compare = 0;
        for (var i = 0; i < sentTag.Length; i++)
          compare |= sentTag[i] ^ calcTag[i]; 

        //if message doesn't authenticate return null
        if (compare != 0)
          return null;

        using (var aes = new AesManaged
        {
          KeySize = KeyBitSize,
          BlockSize = BlockBitSize,
          Mode = CipherMode.CBC,
          Padding = PaddingMode.PKCS7
        })
        {

          //Grab IV from message
          var iv = new byte[ivLength];
          Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length);

          using (var decrypter = aes.CreateDecryptor(cryptKey, iv))
          using (var plainTextStream = new MemoryStream())
          {
            using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write))
            using (var binaryWriter = new BinaryWriter(decrypterStream))
            {
              //Decrypt Cipher Text from Message
              binaryWriter.Write(
                encryptedMessage,
                nonSecretPayloadLength + iv.Length,
                encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length
              );
            }
            //Return Plain Text
            return plainTextStream.ToArray();
          }
        }
      }
    }

    public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
    {
      nonSecretPayload = nonSecretPayload ?? new byte[] {};

      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (secretMessage == null || secretMessage.Length ==0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var payload = new byte[((SaltBitSize / 8) * 2) + nonSecretPayload.Length];

      Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
      int payloadIndex = nonSecretPayload.Length;

      byte[] cryptKey;
      byte[] authKey;
      //Use Random Salt to prevent pre-generated weak password attacks.
      using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
      {
        var salt = generator.Salt;

        //Generate Keys
        cryptKey = generator.GetBytes(KeyBitSize / 8);

        //Create Non Secret Payload
        Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
        payloadIndex += salt.Length;
      }

      //Deriving separate key, might be less efficient than using HKDF, 
      //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF.
      using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
      {
        var salt = generator.Salt;

        //Generate Keys
        authKey = generator.GetBytes(KeyBitSize / 8);

        //Create Rest of Non Secret Payload
        Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
      }

      return SimpleEncrypt(secretMessage, cryptKey, authKey, payload);
    }

    public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cryptSalt = new byte[SaltBitSize / 8];
      var authSalt = new byte[SaltBitSize / 8];

      //Grab Salt from Non-Secret Payload
      Array.Copy(encryptedMessage, nonSecretPayloadLength, cryptSalt, 0, cryptSalt.Length);
      Array.Copy(encryptedMessage, nonSecretPayloadLength + cryptSalt.Length, authSalt, 0, authSalt.Length);

      byte[] cryptKey;
      byte[] authKey;

      //Generate crypt key
      using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations))
      {
        cryptKey = generator.GetBytes(KeyBitSize / 8);
      }
      //Generate auth key
      using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations))
      {
        authKey = generator.GetBytes(KeyBitSize / 8);
      }

      return SimpleDecrypt(encryptedMessage, cryptKey, authKey, cryptSalt.Length + authSalt.Length + nonSecretPayloadLength);
    }
  }
}

Надувной Замок AES-GCM [Суть]

/*
 * This work (Modern Encryption of a String C#, by James Tuley), 
 * identified by James Tuley, is free of known copyright restrictions.
 * https://gist.github.com/4336842
 * http://creativecommons.org/publicdomain/mark/1.0/ 
 */

using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Encryption
{

  public static class AESGCM
  {
    private static readonly SecureRandom Random = new SecureRandom();

    //Preconfigured Encryption Parameters
    public static readonly int NonceBitSize = 128;
    public static readonly int MacBitSize = 128;
    public static readonly int KeyBitSize = 256;

    //Preconfigured Password Key Derivation Parameters
    public static readonly int SaltBitSize = 128;
    public static readonly int Iterations = 10000;
    public static readonly int MinPasswordLength = 12;


    /// <summary>
    /// Helper that generates a random new key on each call.
    /// </summary>
    /// <returns></returns>
    public static byte[] NewKey()
    {
      var key = new byte[KeyBitSize / 8];
      Random.NextBytes(key);
      return key;
    }

    /// <summary>
    /// Simple Encryption And Authentication (AES-GCM) of a UTF8 string.
    /// </summary>
    /// <param name = "secretMessage">The secret message.</param>
    /// <param name = "key">The key.</param>
    /// <param name = "nonSecretPayload">Optional non-secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">Secret Message Required!;secretMessage</exception>
    /// <remarks>
    /// Adds overhead of (Optional-Payload + BlockSize(16) + Message +  HMac-Tag(16)) * 1.33 Base64
    /// </remarks>
    public static string SimpleEncrypt(string secretMessage, byte[] key, byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncrypt(plainText, key, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }


    /// <summary>
    /// Simple Decryption & Authentication (AES-GCM) of a UTF8 Message
    /// </summary>
    /// <param name = "encryptedMessage">The encrypted message.</param>
    /// <param name = "key">The key.</param>
    /// <param name = "nonSecretPayloadLength">Length of the optional non-secret payload.</param>
    /// <returns>Decrypted Message</returns>
    public static string SimpleDecrypt(string encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrEmpty(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecrypt(cipherText, key, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    /// <summary>
    /// Simple Encryption And Authentication (AES-GCM) of a UTF8 String
    /// using key derived from a password (PBKDF2).
    /// </summary>
    /// <param name = "secretMessage">The secret message.</param>
    /// <param name = "password">The password.</param>
    /// <param name = "nonSecretPayload">The non secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// Adds additional non secret payload for key generation parameters.
    /// </remarks>
    public static string SimpleEncryptWithPassword(string secretMessage, string password,
                             byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }


    /// <summary>
    /// Simple Decryption and Authentication (AES-GCM) of a UTF8 message
    /// using a key derived from a password (PBKDF2)
    /// </summary>
    /// <param name = "encryptedMessage">The encrypted message.</param>
    /// <param name = "password">The password.</param>
    /// <param name = "nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref = "System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// </remarks>
    public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
                             int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key, byte[] nonSecretPayload = null)
    {
      //User Error Checks
      if (key == null || key.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");

      if (secretMessage == null || secretMessage.Length == 0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      //Non-secret Payload Optional
      nonSecretPayload = nonSecretPayload ?? new byte[] { };

      //Using random nonce large enough not to repeat
      var nonce = new byte[NonceBitSize / 8];
      Random.NextBytes(nonce, 0, nonce.Length);

      var cipher = new GcmBlockCipher(new AesFastEngine());
      var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
      cipher.Init(true, parameters);

      //Generate Cipher Text With Auth Tag
      var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
      var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
      cipher.DoFinal(cipherText, len);

      //Assemble Message
      using (var combinedStream = new MemoryStream())
      {
        using (var binaryWriter = new BinaryWriter(combinedStream))
        {
          //Prepend Authenticated Payload
          binaryWriter.Write(nonSecretPayload);
          //Prepend Nonce
          binaryWriter.Write(nonce);
          //Write Cipher Text
          binaryWriter.Write(cipherText);
        }
        return combinedStream.ToArray();
      }
    }

    public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (key == null || key.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      using (var cipherStream = new MemoryStream(encryptedMessage))
      using (var cipherReader = new BinaryReader(cipherStream))
      {
        //Grab Payload
        var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);

        //Grab Nonce
        var nonce = cipherReader.ReadBytes(NonceBitSize / 8);

        var cipher = new GcmBlockCipher(new AesFastEngine());
        var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
        cipher.Init(false, parameters);

        //Decrypt Cipher Text
        var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretPayloadLength - nonce.Length);
        var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];  

        try
        {
          var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
          cipher.DoFinal(plainText, len);

        }
        catch (InvalidCipherTextException)
        {
          //Return null if it doesn't authenticate
          return null;
        }

        return plainText;
      }

    }

    public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
    {
      nonSecretPayload = nonSecretPayload ?? new byte[] {};

      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (secretMessage == null || secretMessage.Length == 0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var generator = new Pkcs5S2ParametersGenerator();

      //Use Random Salt to minimize pre-generated weak password attacks.
      var salt = new byte[SaltBitSize / 8];
      Random.NextBytes(salt);

      generator.Init(
        PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
        salt,
        Iterations);

      //Generate Key
      var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

      //Create Full Non Secret Payload
      var payload = new byte[salt.Length + nonSecretPayload.Length];
      Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
      Array.Copy(salt,0, payload,nonSecretPayload.Length, salt.Length);

      return SimpleEncrypt(secretMessage, key.GetKey(), payload);
    }

    public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var generator = new Pkcs5S2ParametersGenerator();

      //Grab Salt from Payload
      var salt = new byte[SaltBitSize / 8];
      Array.Copy(encryptedMessage, nonSecretPayloadLength, salt, 0, salt.Length);

      generator.Init(
        PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
        salt,
        Iterations);

      //Generate Key
      var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

      return SimpleDecrypt(encryptedMessage, key.GetKey(), salt.Length + nonSecretPayloadLength);
    }
  }
}

Разместите эти образцы и на обзор кода.

jbtule 03.12.2012 18:27

Что касается первого примера кода и метода расшифровки ... Есть две вещи, которые я не очень хорошо понимаю и надеюсь, что вы сможете прояснить. При каких обстоятельствах вернется первый оператор if? И цикл for, который проверяет соответствие каждого элемента в массивах ботов: я никогда не видел вашу логику раньше "auth = auth && sentTag [i] == calcTag [i];" есть ли в этом что-то особенное, или я могу выдать false, как только обнаружу, что один байт не соответствует? Извините за расплывчатость, здесь не так много места для комментариев и форматирования кода!

Caster Troy 19.12.2012 03:13

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

jbtule 19.12.2012 17:41

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

jbtule 19.12.2012 17:42

Замечательно, спасибо! Вы бы порекомендовали «Практическую криптографию» Нильса Фергюсона? Кажется, все рекомендуют его для начинающих по криптографии. Все очень хорошо понимают, как реализовать простое шифрование, но больше всего меня интересуют дополнительные меры безопасности, как вы описываете. И последний вопрос, если вы не против; Вы используете AesManaged, который, как я понимаю, не сертифицирован. Могу ли я использовать AesCryptoServiceProvider? Сейчас мне удобнее пользоваться этим классом.

Caster Troy 19.12.2012 23:59

Это хорошая книга, относительно недавняя. Еще больше я бы порекомендовал бесплатный онлайн-курс Криптография I Дэна Боне. Действительно хорошие видео, действительно хорошие викторины, а также действительно хорошие машинные проблемы, которые обеспечивают хорошую практическую основу для использования криптографии. Вы должны использовать то, что вам удобнее всего в отношении AesCryptoServiceProvider.

jbtule 20.12.2012 00:08

Для чего в AESThenHMAC.SimpleEncryptWithPassword() несекретная полезная нагрузка?

Jan 15.10.2014 13:53

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

jbtule 15.10.2014 17:47

Пример SimpleEncrypt для BouncyCastle дает мне ошибку компиляции на Random.NextBytes(nonce, 0, nonce.Length);, говоря, что он принимает только один аргумент.

Kyle 30.10.2014 21:25

Ну, это неправда, и не дает мне никаких предупреждений компилятора. github.com/bcgit/bc-csharp/blob/master/crypto/src/security/… Вы вносили какие-либо изменения или исключили что-либо при копировании и вставке?

jbtule 30.10.2014 23:13

Спасибо за отличный ответ. У меня есть общий вопрос об этом сценарии шифрования - добавление HMAC позволяет держателю ключа аутентификации проверить, что первый раздел (несекретная часть + зашифрованное сообщение AES IV +) был подписан другой стороной, которая имеет такой же ключ аутентификации. . Однако в этом случае, если у вас есть две стороны, которые общаются (и игнорируют часть обычного текста на секунду), не является ли простой факт их способности расшифровать зашифрованный раздел AES столь же надежной гарантией? Что еще HMAC покупает вам (или только для аутентификации незашифрованной части)?

Benji XVI 19.11.2014 22:25

@BenjiXVI нет, это не сильная гарантия, см. Выше комментарий об аутентифицированном шифровании и атаках с выбранным зашифрованным текстом.

jbtule 02.12.2014 02:32

Прошу прощения за глупость следующих вопросов. 1) Как или что рекомендуется для хранения ключей шифрования и аутентификации для решения AESThenHMAC? 2) Не стоит ли использовать одни и те же ключи шифрования и аутентификации в течение всего срока службы приложения? Я рассматриваю возможность использования этого кода для шифрования конфиденциальных данных перед их сохранением в моей базе данных.

Parth Shah 30.12.2014 07:03

@ParthShah Это не глупый вопрос, и на самом деле его часто упускают из виду. Хранить ключи - ТРУДНО-очень. Не существует идеального способа без специализированного оборудования, поэтому просто сделайте все возможное, чтобы сохранить его как можно дальше от вашего зашифрованного текста (в этом случае не храните ключи (или ключи для дешифрования ключей) в той же базе данных, что и по крайней мере, зашифрованный текст). Смена ключей в течение срока службы приложения включает в себя больше, чем приведенный выше код, но доступна для dotnet в виде библиотеки: github.com/jbtule/keyczar-dotnet

jbtule 30.12.2014 17:02

@jbtule: SimpleDecrypt и SimpleDecryptWithPassword AESThenHMAC.cs Gist пытаются вернуть «открытый текст» в нижнем регистре, а не в верблюжьем регистре, как заявлено.

olingern 03.06.2015 22:20

Красиво объясненный раздел использования был бы чрезвычайно полезен.

Rocklan 28.09.2015 09:42

RandomNumberGenerator не является потокобезопасным; рассмотрите вместо этого RNGCryptoServiceProvider. И во-вторых, необходимость в разделе использования!

TrueWill 11.11.2015 18:45

@TrueWill RandomNumberGenerator - это абстрактный класс, System.Security.Cryptography, RandomNumberGenerate.Create () фактически возвращает RNGCryptoServiceProvider -stackoverflow.com/a/12329913/637783. Я думаю о примере использования.

jbtule 11.11.2015 19:04

Обратите внимание, что при Iterations = 10000 и строковом пароле это может быть неприемлемо медленным для определенных сценариев.

TrueWill 13.11.2015 22:21

@TrueWill Это правда, это 10000, потому что он должен быть медленным. Лучше нет использовать сгенерированные паролем ключи, когда важна производительность. Ключи, производные от пароля, не идеальны для большинства сценариев, я добавил их в качестве опции в этом примере, потому что в большинстве примеров шифрования SO используется пароль: '(поэтому я хотел предоставить лучшие методы обеспечения безопасности для работы с ними.

jbtule 13.11.2015 22:29

Какое из двух решений вы бы порекомендовали? Поскольку вы называете свое второе решение более простой практикой, предполагаем ли мы, что это означает, что оно технически хуже, но быстрее первого решения?

rdans 20.05.2016 14:10

@rdans технически не хуже, наоборот, его проще использовать, имея аутентификацию и шифрование за один шаг. Недостатком второго решения является то, что вам нужна дополнительная зависимость, и это единственная причина, по которой я предоставил первое решение.

jbtule 20.05.2016 15:31

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

Mark Sowul 16.12.2016 19:49

Я хочу внести свой вклад в мой код для алгоритма AES Rfc2898DeriveBytes (здесь в документации), написанный на C# (.NET framework 4) и полностью работающий также для ограниченных платформ, например .NET Compact Framework для Windows Phone 7.0+ (не все платформы поддерживают все криптографические методы .NET framework!).

Надеюсь, это поможет кому угодно!

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

public static class Crypto
{
    private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x0e, 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);
                        encrypt.Write(cleanText, 0, cleanText.Length);
                        encrypt.FlushFinalBlock();
                    }

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


                    return encryptedText;
                }
            }
        }
        catch
        {
            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);


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

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


                    return decryptedText;
                }
            }
        }
        catch
        {
            return String.Empty;
        }
        }
    }
}

1) Почему вы используете переменную IVa, которая не является IV, а является паролем? 2) Почему вы устанавливаете IV = Key? Вам нужен новый случайный IV для каждого шифрования. 3) Отсутствие MAC позволяет дополнить атаки оракула

CodesInChaos 14.08.2013 20:44

НадувнойЗамок - отличная библиотека Crypto для .NET, она доступна в виде пакета Nuget для установки в ваши проекты. Мне это нравится намного больше, чем то, что сейчас доступно в библиотеке System.Security.Cryptography. Это дает вам гораздо больше возможностей с точки зрения доступных алгоритмов и предоставляет больше режимов для этих алгоритмов.

Это пример реализации TwoFish, который был написан Брюс Шнайер (герой всех нас, параноиков). Это симметричный алгоритм, подобный Rijndael (он же AES). Он был одним из трех финалистов стандарта AES и родственником другого известного алгоритма, написанного Брюсом Шнайером, под названием BlowFish.

Первое, что нужно сделать с bouncycastle, - это создать класс шифратора, это упростит реализацию других блочных шифров в библиотеке. Следующий класс шифратора принимает общий аргумент T, где T реализует IBlockCipher и имеет конструктор по умолчанию.

Обновлено: По многочисленным просьбам я решил реализовать создание случайного IV, а также включить HMAC в этот класс. Хотя с точки зрения стиля это противоречит принципу SOLID единственной ответственности, я изменил характер того, что делает этот класс. Этот класс теперь будет принимать два общих параметра: один для шифра, а другой для дайджеста. Он автоматически генерирует IV с использованием RNGCryptoServiceProvider для обеспечения хорошей энтропии RNG и позволяет вам использовать любой алгоритм дайджеста, который вы хотите от BouncyCastle для генерации MAC.

using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;

public sealed class Encryptor<TBlockCipher, TDigest>
    where TBlockCipher : IBlockCipher, new()
    where TDigest : IDigest, new()
{
    private Encoding encoding;

    private IBlockCipher blockCipher;

    private BufferedBlockCipher cipher;

    private HMac mac;

    private byte[] key;

    public Encryptor(Encoding encoding, byte[] key, byte[] macKey)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, new Pkcs7Padding());
    }

    public Encryptor(Encoding encoding, byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, padding);
    }

    private void Init(byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.blockCipher = new CbcBlockCipher(new TBlockCipher());
        this.cipher = new PaddedBufferedBlockCipher(this.blockCipher, padding);
        this.mac = new HMac(new TDigest());
        this.mac.Init(new KeyParameter(macKey));
    }

    public string Encrypt(string plain)
    {
        return Convert.ToBase64String(EncryptBytes(plain));
    }

    public byte[] EncryptBytes(string plain)
    {
        byte[] input = this.encoding.GetBytes(plain);

        var iv = this.GenerateIV();

        var cipher = this.BouncyCastleCrypto(true, input, new ParametersWithIV(new KeyParameter(key), iv));
        byte[] message = CombineArrays(iv, cipher);

        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        byte[] digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(digest, 0);

        var result = CombineArrays(digest, message);
        return result;
    }

    public byte[] DecryptBytes(byte[] bytes)
    {
        // split the digest into component parts
        var digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        var message = new byte[bytes.Length - digest.Length];
        var iv = new byte[this.blockCipher.GetBlockSize()];
        var cipher = new byte[message.Length - iv.Length];

        Buffer.BlockCopy(bytes, 0, digest, 0, digest.Length);
        Buffer.BlockCopy(bytes, digest.Length, message, 0, message.Length);
        if (!IsValidHMac(digest, message))
        {
            throw new CryptoException();
        }

        Buffer.BlockCopy(message, 0, iv, 0, iv.Length);
        Buffer.BlockCopy(message, iv.Length, cipher, 0, cipher.Length);

        byte[] result = this.BouncyCastleCrypto(false, cipher, new ParametersWithIV(new KeyParameter(key), iv));
        return result;
    }

    public string Decrypt(byte[] bytes)
    {
        return this.encoding.GetString(DecryptBytes(bytes));
    }

    public string Decrypt(string cipher)
    {
        return this.Decrypt(Convert.FromBase64String(cipher));
    }

    private bool IsValidHMac(byte[] digest, byte[] message)
    {
        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        byte[] computed = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(computed, 0);

        return AreEqual(digest,computed);
    }

    private static bool AreEqual(byte [] digest, byte[] computed)
    {
        if (digest.Length != computed.Length)
        {
            return false;
        }

        int result = 0;
        for (int i = 0; i < digest.Length; i++)
        {
            // compute equality of all bytes before returning.
            //   helps prevent timing attacks: 
            //   https://codahale.com/a-lesson-in-timing-attacks/
            result |= digest[i] ^ computed[i];
        }

        return result == 0;
    }

    private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, ICipherParameters parameters)
    {
        try
        {
            cipher.Init(forEncrypt, parameters);

            return this.cipher.DoFinal(input);
        }
        catch (CryptoException)
        {
            throw;
        }
    }

    private byte[] GenerateIV()
    {
        using (var provider = new RNGCryptoServiceProvider())
        {
            // 1st block
            byte[] result = new byte[this.blockCipher.GetBlockSize()];
            provider.GetBytes(result);

            return result;
        }
    }

    private static byte[] CombineArrays(byte[] source1, byte[] source2)
    {
        byte[] result = new byte[source1.Length + source2.Length];
        Buffer.BlockCopy(source1, 0, result, 0, source1.Length);
        Buffer.BlockCopy(source2, 0, result, source1.Length, source2.Length);

        return result;
    }
}

Затем просто вызовите методы encrypt и decrypt в новом классе, вот пример с использованием twofish:

var encrypt = new Encryptor<TwofishEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);

string cipher = encrypt.Encrypt("TEST");   
string plainText = encrypt.Decrypt(cipher);

Так же легко заменить другой блочный шифр, например TripleDES:

var des = new Encryptor<DesEdeEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);

string cipher = des.Encrypt("TEST");
string plainText = des.Decrypt(cipher);

Наконец, если вы хотите использовать AES с SHA256 HMAC, вы можете сделать следующее:

var aes = new Encryptor<AesEngine, Sha256Digest>(Encoding.UTF8, key, hmacKey);

cipher = aes.Encrypt("TEST");
plainText = aes.Decrypt(cipher);

Самая сложная часть шифрования связана с ключами, а не с алгоритмами. Вам нужно будет подумать о том, где вы храните свои ключи, и, если нужно, как вы их обмениваете. Все эти алгоритмы выдержали испытание временем, и их чрезвычайно сложно сломать. Тот, кто хочет украсть у вас информацию, не собирается тратить вечность на криптоанализ ваших сообщений, он попытается выяснить, что или где находится ваш ключ. Итак, # 1 выбирайте ключи с умом, # 2 храните их в надежном месте, если вы используете web.config и IIS, тогда вы можете зашифровать части файла web.config, и, наконец, если вам нужно обмениваться ключами, убедитесь, что ваш протокол для обмена ключа безопасен .

Обновление 2 Изменен метод сравнения для защиты от временных атак. Подробнее см. Здесь http://codahale.com/a-lesson-in-timing-attacks/. Также обновлено значение по умолчанию для заполнения PKCS7 и добавлен новый конструктор, позволяющий конечному пользователю выбирать, какое заполнение они хотели бы использовать. Спасибо @CodesInChaos за предложения.

1) Использование этого класса довольно неприятно, поскольку вы возлагаете бремя управления IV на пользователя, который почти наверняка ошибается. 2) Отсутствие MAC делает это уязвимым для оракулов заполнения.

CodesInChaos 14.08.2013 20:47

1) Мне кажется, что ваша набивка сломана. Вы добавляете нулевой отступ и не удаляете его. Нулевое заполнение - плохая идея, поскольку его нельзя надежно удалить. Вместо этого используйте заполнение PKCS # 7. Я ожидал, что функция шифрования / дешифрования bouncycastle уже поддерживает это. 2) Вы должны использовать постоянное сравнение времени для проверки MAC, а не SequenceEqual. Это позволяет избежать временного побочного канала, по которому происходит утечка информации о том, как долго префикс представленного MAC и фактического MAC совпадают.

CodesInChaos 10.03.2014 21:08

@CodesInChaos Я согласен, спасибо за проверку. Я внес правку, чтобы решить эту пару проблем. - ботан

nerdybeardo 11.03.2014 14:10

отличный ответ, всего один вопрос .... какой будет ключ и hmacKey, я новичок в криптографии ... спасибо!

Terkhos 24.07.2015 18:35

@Terkhos Вы должны использовать безопасный генератор случайных чисел для генерации ключей, например RNGCryptoServiceProvider, вы никогда не должны использовать парольную фразу или что-то предсказуемое. Вы также должны использовать максимальную длину, которую предоставит алгоритм, например, AES 256 использует размер ключа, равный 256 битам, поэтому лучше всего будет 32 случайных байта, размеры ключей HMAC обычно основаны на размере алгоритма, например SHA2 ( 256) 256-битного ключа, сгенерированного безопасным генератором случайных чисел, будет достаточно. Часто меняйте ключи! Чем чаще, тем лучше!

nerdybeardo 27.07.2015 04:07

происходит ли класс HMAC из System.Security.Cryptography или Org.BouncyCastle.Crypto.Macs?

ps2goat 21.05.2016 16:35

Если вы используете ASP.Net, теперь вы можете использовать встроенные функции в .Net 4.0 и новее.

System.Web.Security.MachineKey

.Net 4.5 имеет MachineKey.Protect() и MachineKey.Unprotect().

.Net 4.0 имеет MachineKey.Encode() и MachineKey.Decode(). Вам просто нужно установить для MachineKeyProtection значение «All».

Вне ASP.Net этот класс, кажется, генерирует новый ключ при каждом перезапуске приложения, поэтому не работает. При беглом взгляде на ILSpy мне кажется, что он генерирует свои собственные значения по умолчанию, если отсутствуют соответствующие настройки app.settings. Таким образом, вы действительно можете настроить его за пределами ASP.Net.

Мне не удалось найти эквивалент не -ASP.Net за пределами пространства имен System.Web.

хм, может кто-нибудь сказать мне, почему за этот ответ так мало голосов? Похоже, очень удобный способ для приложений ASP.NET

Dirk Boer 27.03.2014 16:04

@DirkBoer Функциональность была добавлена ​​через пару лет после того, как вопрос был задан, я добавил свой ответ на этот вопрос, чтобы люди знали, что сегодня есть более простые способы. Это также работает только с ASP.Net без некоторого app.config-fu, что довольно опасно, если вы не знаете, что делаете.

mattmanser 27.03.2014 16:38

Простите за незнание, но на веб-странице я не могу понять свой ответ. Если я зашифрую строку на одной машине, запишу ее в базу данных и прочитаю на другой машине, смогу ли я расшифровать ее, если параметры целей имеют одинаковое значение? Может меня просто смущает название класса "MachineKey"

Adriaan Davel 24.02.2016 18:33

Другой вопрос, могу ли я использовать это в приложении WPF? У него нет веб-ссылок, можно ли добавить ссылки на System.Web?

Adriaan Davel 24.02.2016 18:57

@AdriaanDavel Да, но имейте в виду, что я думаю, что он генерирует новый машинный ключ при каждом запуске приложения, поэтому в зависимости от того, для чего вы его используете, я думаю, вам нужно где-то сохранить свой собственный машинный ключ.

mattmanser 25.02.2016 12:53

@AdriaanDavel Согласно связанным документам, «API MachineKey должны использоваться только в приложении ASP.NET. Поведение API MachineKey вне контекста приложения ASP.NET не определено» - используйте его, только если вам нравится игра Русская рулетка

Mark Sowul 19.12.2016 22:16

Машинный ключ - это именно то, что специфично для машины. Бесполезно, если вы хотите иметь детерминированный метод шифрования и дешифрования одного и того же пароля на разных машинах.

Chad Lehman 05.02.2020 00:09

Это класс, который разместил сюда Бретт. Однако я сделал небольшое изменение, так как получал ошибку «Недопустимая длина для массива символов Base-64» при использовании его для строк URL для шифрования и дешифрования.

public class CryptoURL
{
    private static byte[] _salt = Encoding.ASCII.GetBytes("Catto_Salt_Enter_Any_Value99");

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match. 
    /// The SharedSecret for the Password Reset that is used is in the next line
    ///  string sharedSecret = "OneUpSharedSecret9";
    /// </summary>
    /// <param name = "plainText">The text to encrypt.</param>
    /// <param name = "sharedSecret">A password used to generate a key for encryption.</param>
    public static string EncryptString(string plainText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        string outStr = null;                       // Encrypted string to return
        RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create a RijndaelManaged object
            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            // Create a decryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // prepend the IV
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                }

                outStr = HttpServerUtility.UrlTokenEncode(msEncrypt.ToArray());
                //outStr = Convert.ToBase64String(msEncrypt.ToArray());
                // you may need to add a reference. right click reference in solution explorer => "add Reference" => .NET tab => select "System.Web"
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    /// <param name = "cipherText">The text to decrypt.</param>
    /// <param name = "sharedSecret">A password used to generate a key for decryption.</param>
    public static string DecryptString(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        byte[] inputByteArray;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create the streams used for decryption.                
            //byte[] bytes = Convert.FromBase64String(cipherText);
            inputByteArray = HttpServerUtility.UrlTokenDecode(cipherText);

            using (MemoryStream msDecrypt = new MemoryStream(inputByteArray))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        catch (System.Exception ex)
        {
            return "ERROR";
            //throw ex;

        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

    static string ConvertStringArrayToString(string[] array)
    {
        //
        // Concatenate all the elements into a StringBuilder.
        //
        StringBuilder builder = new StringBuilder();
        foreach (string value in array)
        {
            builder.Append(value);
            builder.Append('.');
        }
        return builder.ToString();
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }

}

Для чего используется метод ConvertStringArrayToString()?

abenci 14.09.2016 15:49

            using System;
            using System.Collections.Generic;
            using System.Text;
            using System.Text.RegularExpressions;  // This is for password validation
            using System.Security.Cryptography;
            using System.Configuration;  // This is where the hash functions reside

            namespace BullyTracker.Common
            {
                public class HashEncryption
                {
                    //public string GenerateHashvalue(string thisPassword)
                    //{
                    //    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
                    //    byte[] tmpSource;
                    //    byte[] tmpHash;

                    //    tmpSource = ASCIIEncoding.ASCII.GetBytes(thisPassword); // Turn password into byte array
                    //    tmpHash = md5.ComputeHash(tmpSource);

                    //    StringBuilder sOutput = new StringBuilder(tmpHash.Length);
                    //    for (int i = 0; i < tmpHash.Length; i++)
                    //    {
                    //        sOutput.Append(tmpHash[i].ToString("X2"));  // X2 formats to hexadecimal
                    //    }
                    //    return sOutput.ToString();
                    //}
                    //public Boolean VerifyHashPassword(string thisPassword, string thisHash)
                    //{
                    //    Boolean IsValid = false;
                    //    string tmpHash = GenerateHashvalue(thisPassword); // Call the routine on user input
                    //    if (tmpHash == thisHash) IsValid = true;  // Compare to previously generated hash
                    //    return IsValid;
                    //}
                    public string GenerateHashvalue(string toEncrypt, bool useHashing)
                    {
                        byte[] keyArray;
                        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                        System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
                        // Get the key from config file
                        string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
                        //System.Windows.Forms.MessageBox.Show(key);
                        if (useHashing)
                        {
                            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                            hashmd5.Clear();
                        }
                        else
                            keyArray = UTF8Encoding.UTF8.GetBytes(key);

                        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                        tdes.Key = keyArray;
                        tdes.Mode = CipherMode.ECB;
                        tdes.Padding = PaddingMode.PKCS7;

                        ICryptoTransform cTransform = tdes.CreateEncryptor();
                        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                        tdes.Clear();
                        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
                    }
                    /// <summary>
                    /// DeCrypt a string using dual encryption method. Return a DeCrypted clear string
                    /// </summary>
                    /// <param name = "cipherString">encrypted string</param>
                    /// <param name = "useHashing">Did you use hashing to encrypt this data? pass true is yes</param>
                    /// <returns></returns>
                    public string Decrypt(string cipherString, bool useHashing)
                    {
                        byte[] keyArray;
                        byte[] toEncryptArray = Convert.FromBase64String(cipherString);

                        System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
                        //Get your key from config file to open the lock!
                        string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));

                        if (useHashing)
                        {
                            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                            hashmd5.Clear();
                        }
                        else
                            keyArray = UTF8Encoding.UTF8.GetBytes(key);

                        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                        tdes.Key = keyArray;
                        tdes.Mode = CipherMode.ECB;
                        tdes.Padding = PaddingMode.PKCS7;

                        ICryptoTransform cTransform = tdes.CreateDecryptor();
                        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

                        tdes.Clear();
                        return UTF8Encoding.UTF8.GetString(resultArray);
                    }


                }

            }

Действительно низкого качества. 1) режим ECB (что тоже подразумевает отсутствие IV) 2) 3DES 3) путает ключи и пароли. 4) Плохое название 5) Нет MAC

CodesInChaos 10.04.2014 14:28

для простоты я сделал для себя эту функцию, которую использую не в криптографических целях: замените yourpassphrase своим паролем ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

 namespace My
{
    public class strCrypto
    {
        // This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
    // This size of the IV (in bytes) must = (keysize / 8).  Default keysize is 256, so the IV must be
    // 32 bytes long.  Using a 16 character string here gives us 32 bytes when converted to a byte array.
    private const string initVector = "r5dm5fgm24mfhfku";
    private const string passPhrase = "yourpassphrase"; // email password encryption password

    // This constant is used to determine the keysize of the encryption algorithm.
    private const int keysize = 256;

    public static string encryptString(string plainText)
    {
        //if the plaintext  is empty or null string just return an empty string
        if (plainText == "" || plainText == null )
        {
            return "";
        }

        byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
        byte[] keyBytes = password.GetBytes(keysize / 8);
        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;
        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
        cryptoStream.FlushFinalBlock();
        byte[] cipherTextBytes = memoryStream.ToArray();
        memoryStream.Close();
        cryptoStream.Close();
        return Convert.ToBase64String(cipherTextBytes);
    }

    public static string decryptString(string cipherText)
    {
        //if the ciphertext is empty or null string just return an empty string
        if (cipherText == "" || cipherText == null )
        {
            return "";
        }

        byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
        byte[] keyBytes = password.GetBytes(keysize / 8);
        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;
        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
        CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
        byte[] plainTextBytes = new byte[cipherTextBytes.Length];
        int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
        memoryStream.Close();
        cryptoStream.Close();
        return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
    }


}

}

1) Нет соли в ключевом выводе 2) Константа IV, которая упускает из виду весь смысл IV. Он должен быть разным для каждого шифрования. 3) Отсутствие аутентификации => оракулы заполнения представляют собой угрозу. 4) encryptor.TransformFinalBlock проще, чем использование этой памяти и криптопотоков.

CodesInChaos 23.04.2014 20:09

Вот простой пример шифрования строк на C# с использованием режима AES CBC со случайными IV и HMAC и ключами, полученными из пароля, чтобы показать основные движущиеся части:

private byte[] EncryptBytes(byte[] key, byte[] plaintext)
{
    using (var cipher = new RijndaelManaged { Key = key })
    {
        using (var encryptor = cipher.CreateEncryptor())
        {
            var ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length);

            // IV is prepended to ciphertext
            return cipher.IV.Concat(ciphertext).ToArray();
        }
    }
}

private byte[] DecryptBytes(byte[] key, byte[] packed)
{
    using (var cipher = new RijndaelManaged { Key = key })
    {
        int ivSize = cipher.BlockSize / 8;

        cipher.IV = packed.Take(ivSize).ToArray();

        using (var encryptor = cipher.CreateDecryptor())
        {
            return encryptor.TransformFinalBlock(packed, ivSize, packed.Length - ivSize);
        }
    }
}

private byte[] AddMac(byte[] key, byte[] data)
{
    using (var hmac = new HMACSHA256(key))
    {
        var macBytes = hmac.ComputeHash(data);

        // HMAC is appended to data
        return data.Concat(macBytes).ToArray();
    }
}

private bool BadMac(byte[] found, byte[] computed)
{
    int mismatch = 0;

    // Aim for consistent timing regardless of inputs
    for (int i = 0; i < found.Length; i++)
    {
        mismatch += found[i] == computed[i] ? 0 : 1;
    }

    return mismatch != 0;
}

private byte[] RemoveMac(byte[] key, byte[] data)
{
    using (var hmac = new HMACSHA256(key))
    {
        int macSize = hmac.HashSize / 8;

        var packed = data.Take(data.Length - macSize).ToArray();

        var foundMac = data.Skip(packed.Length).ToArray();

        var computedMac = hmac.ComputeHash(packed);

        if (this.BadMac(foundMac, computedMac))
        {
            throw new Exception("Bad MAC");
        }

        return packed;
    }            
}

private List<byte[]> DeriveTwoKeys(string password)
{
    var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    var kdf = new Rfc2898DeriveBytes(password, salt, 10000);

    var bytes = kdf.GetBytes(32); // Two keys 128 bits each

    return new List<byte[]> { bytes.Take(16).ToArray(), bytes.Skip(16).ToArray() };
}

public byte[] EncryptString(string password, String message)
{
    var keys = this.DeriveTwoKeys(password);

    var plaintext = Encoding.UTF8.GetBytes(message);

    var packed = this.EncryptBytes(keys[0], plaintext);

    return this.AddMac(keys[1], packed);
}

public String DecryptString(string password, byte[] secret)
{
    var keys = this.DeriveTwoKeys(password);

    var packed = this.RemoveMac(keys[1], secret);

    var plaintext = this.DecryptBytes(keys[0], packed);

    return Encoding.UTF8.GetString(plaintext);
}

public void Example()
{
    var password = "correcthorsebatterystaple";

    var secret = this.EncryptString(password, "Hello World");

    Console.WriteLine("secret: " + BitConverter.ToString(secret));

    var recovered = this.DecryptString(password, secret);

    Console.WriteLine(recovered);
}

Пара проблем: 1) Вы не используете соль при выводе ключа, позволяя атаковать несколько целей. 2) Ваша функция сравнения MAC-адресов потенциально уязвима для атак по побочному каналу / времени, поскольку вы переходите на секретные данные. Вместо этого используйте что-нибудь вроде mismatch += found[i]^computed[i]. 3) Вы используете более 20 байт PBKDF2-HMAC-SHA-1, который замедляет ваш KDF в 2 раза, не замедляя злоумышленника.

CodesInChaos 11.08.2014 14:59

@CodesInChaos: 1) Это было задумано как простой пример для начала - я опускаю случайную соль только для ясности. Но хороший момент. 2) Хороший, тонкий момент. 3) Что вы предлагаете, чтобы получить два 16-байтовых ключа из двадцати байтов?

Jim Flood 12.08.2014 00:47

Самый простой способ - это хеширование вывода медленного хэша с помощью SHA-2. Более изящные способы - это HKDF или просто снова применить PBKDF2, но на этот раз с итерациями, установленными на 1.

CodesInChaos 12.08.2014 12:00

@CodesInChaos Я бы не стал использовать SHA-2. Работа хеш-функции не совпадает с работой функции деривации ключа. Хеш должен быть только непредсказуемым и изменяться при изменении ввода. Ключ должен быть неотличим от случайного. Я бы все равно взял из KDF 32 байта. В этом случае вы оптимизируете слишком рано и увеличиваете риск.

Jim Flood 14.08.2014 20:27

Скопировал в моем отвечать здесь из аналогичного вопроса: Простое двустороннее шифрование для C#.

Основано на нескольких ответах и ​​комментариях.

  • К зашифрованному тексту добавлен случайный вектор инициализации (@jbtule)
  • Используйте TransformFinalBlock () вместо MemoryStream (@RenniePet)
  • Нет предварительно заполненных ключей, чтобы никто не копировал и вставлял катастрофу
  • Правильная утилизация и использование шаблонов

Код:

/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in https://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

    /// <summary>
    ///     Must be exactly 16, 24 or 32 characters long.
    /// </summary>
    private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 16, 24 OR 32 CHARS");

    private readonly UTF8Encoding _encoder;
    private readonly ICryptoTransform _encryptor;
    private readonly RijndaelManaged _rijndael;

    public SimpleAes()
    {
        _rijndael = new RijndaelManaged {Key = Key};
        _rijndael.GenerateIV();
        _encryptor = _rijndael.CreateEncryptor();
        _encoder = new UTF8Encoding();
    }

    public string Decrypt(string encrypted)
    {
        return _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    public void Dispose()
    {
        _rijndael.Dispose();
        _encryptor.Dispose();
    }

    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
    }

    private byte[] Decrypt(byte[] buffer)
    {
        // IV is prepended to cryptotext
        byte[] iv = buffer.Take(IvBytes).ToArray();
        using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
        {
            return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes);
        }
    }

    private byte[] Encrypt(byte[] buffer)
    {
        // Prepend cryptotext with IV
        byte[] inputBuffer = _rijndael.IV.Concat(buffer).ToArray();
        return _encryptor.TransformFinalBlock(inputBuffer, IvBytes, buffer.Length);
    }
}

Вы должны добавить MAC для предотвращения активных атак, таких как оракулы заполнения.

CodesInChaos 20.02.2015 00:31

Вы, наверное, правы, я никоим образом не разбираюсь в этой области. Когда я впервые посетил эту тему, я просто хотел что-то простое, работающее и достаточно безопасное. Я бы определенно использовал проверенную библиотеку для очень конфиденциальных данных.

angularsen 20.02.2015 10:36

Вы должны использовать пространство имен using System.Security.Cryptography; а useHashing - это тип bool либо true, либо false. Строковая переменная key должна быть одинаковой для шифрования и дешифрования.

//Encryption
public string EncryptText(string toEncrypt, bool useHashing)
    {
        try
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

            string key = "String Key Value"; //Based on this key stirng is encrypting
            //System.Windows.Forms.MessageBox.Show(key);
            //If hashing use get hashcode regards to your key
            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                //Always release the resources and flush data
                //of the Cryptographic service provide. Best Practice

                hashmd5.Clear();
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(key);

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            //set the secret key for the tripleDES algorithm
            tdes.Key = keyArray;
            //mode of operation. there are other 4 modes. We choose ECB(Electronic code Book)
            tdes.Mode = CipherMode.ECB;
            //padding mode(if any extra byte added)
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            //transform the specified region of bytes array to resultArray
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,          toEncryptArray.Length);
            //Release resources held by TripleDes Encryptor
            tdes.Clear();
            //Return the encrypted data into unreadable string format
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    //Decryption
    public string DecryptText(string cipherString, bool useHashing)
    {

        try
        {
            byte[] keyArray;
            //get the byte code of the string

            byte[] toEncryptArray = Convert.FromBase64String(cipherString);

            string key = "String Key Value"; //Based on this key string is decrypted

            if (useHashing)
            {
                //if hashing was used get the hash code with regards to your key
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                //release any resource held by the MD5CryptoServiceProvider

                hashmd5.Clear();
            }
            else
            {
                //if hashing was not implemented get the byte code of the key
                keyArray = UTF8Encoding.UTF8.GetBytes(key);
            }

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            //set the secret key for the tripleDES algorithm
            tdes.Key = keyArray;
            //mode of operation. there are other 4 modes.
            //We choose ECB(Electronic code Book)

            tdes.Mode = CipherMode.ECB;
            //padding mode(if any extra byte added)
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock
                    (toEncryptArray, 0, toEncryptArray.Length);
            //Release resources held by TripleDes Encryptor
            tdes.Clear();
            //return the Clear decrypted TEXT
            return UTF8Encoding.UTF8.GetString(resultArray);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

-1 1) Режим ECB очень слабый 2) Отсутствие MAC оставляет вас уязвимым для активных атак, таких как оракулы заполнения. 3) Почему вы все еще используете 3DES в наши дни? Он не сломан, но AES явно лучший выбор.

CodesInChaos 20.02.2015 00:34

Вот простой сниппет, изначально созданный сниппетами ASP

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


 private string Encrypt(string clearText)
    {
        string EncryptionKey = "yourkey";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                clearText = Convert.ToBase64String(ms.ToArray());
            }
        }
        return clearText;
    }

 private string Decrypt(string cipherText)
    {
        string EncryptionKey = "yourkey";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

Вы не проверяете целостность / аутентификацию. Вы должны добавить MAC.

Artjom B. 17.12.2014 12:18

То, что вы в точности имеете в виду, на самом деле приведенный выше пример - это зашифровать / расшифровать строковую переменную.

AirCodeOne 06.01.2015 08:01

Зашифрованный текст должен быть аутентифицирован (например, с помощью HMAC) для защиты от атак оракула с заполнением. Когда я снова просматриваю этот код, кажется, что вы используете режим ECB, который никогда не следует использовать, потому что он не является семантически безопасным. Кроме того, когда вы получаете ключ и IV из главного ключа и соли, соль статична. Это приводит к статическому IV, который нарушает всю концепцию IV и снова делает вашу схему семантически небезопасной.

Artjom B. 06.01.2015 12:33

Спасибо, брат, было бы здорово, если бы вы могли передать здесь исправленный код.

AirCodeOne 09.01.2015 05:35

Шифрование

public string EncryptString(string inputString)
{
    MemoryStream memStream = null;
    try
    {
        byte[] key = { };
        byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 };
        string encryptKey = "aXb2uy4z"; // MUST be 8 characters
        key = Encoding.UTF8.GetBytes(encryptKey);
        byte[] byteInput = Encoding.UTF8.GetBytes(inputString);
        DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
        memStream = new MemoryStream();
        ICryptoTransform transform = provider.CreateEncryptor(key, IV);
        CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
        cryptoStream.Write(byteInput, 0, byteInput.Length);
        cryptoStream.FlushFinalBlock();
    }
    catch (Exception ex)
    {
        Response.Write(ex.Message);
    }
    return Convert.ToBase64String(memStream.ToArray());
}

Расшифровка:

public string DecryptString(string inputString)
{
    MemoryStream memStream = null;
    try
    {
        byte[] key = { };
        byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 };
        string encryptKey = "aXb2uy4z"; // MUST be 8 characters
        key = Encoding.UTF8.GetBytes(encryptKey);
        byte[] byteInput = new byte[inputString.Length];
        byteInput = Convert.FromBase64String(inputString);
        DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
        memStream = new MemoryStream();
        ICryptoTransform transform = provider.CreateDecryptor(key, IV);
        CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
        cryptoStream.Write(byteInput, 0, byteInput.Length);
        cryptoStream.FlushFinalBlock();
    }
    catch (Exception ex)
    {
        Response.Write(ex.Message);
    }

    Encoding encoding1 = Encoding.UTF8;
    return encoding1.GetString(memStream.ToArray());
}

-1 Это очень слабо. 1) DES легко подобрать перебором, имея 56-битный ключ. 2) Ключ двоичный, а не UTF8. Если ключ состоит из символов ASCII (вероятно, на практике), это уменьшает эффективный размер ключа до 48 бит. 3) IV должен быть разным для каждого сообщения. 4) Отсутствие MAC оставляет вас уязвимым для активных атак, включая оракулы заполнения.

CodesInChaos 20.02.2015 00:29

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

Roland 28.10.2015 18:47

-1 @Roland, как упоминалось CodesInChaos, IV должен быть разным для каждого сообщения, очень просто, если это не так, вы неправильно используете API, поэтому этот код никогда не следует использовать. Период. Чтобы не затмевать 48-битный ключ, он может быть расшифрован кем угодно без ключа всего за день, так что это больше не шифрование и, следовательно, не отвечает на вопрос.

jbtule 29.03.2017 20:29
Предупреждение безопасности: не используйте этот код See above Comment by @CodesInChaos
jbtule 09.05.2017 00:59
Используйте это для простых приложений If you are guarding nuclear secrets, use something else. This works as is.
John Pittaway 28.09.2019 02:30

Более подробную информацию об этом методе от Microsoft можно найти в разделе docs.microsoft.com/en-us/dotnet/standard/security/….

Qben 01.10.2020 12:21

Хороший алгоритм для безопасного хеширования данных - BCrypt:

Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.

Есть хороший Реализация BCrypt в .NET, который также доступен как пакет NuGet.

Вопрос спрашивает, как зашифровать и расшифровать строку. Если я не упускаю чего-то огромного - как можно расшифровать строку в BCrypt? BCrypt, несмотря на свое название, является функцией хеширования.

The1nk 01.07.2016 18:50

Заявление об отказе от ответственности: это решение следует использовать только для данных в состоянии покоя, которые не являются общедоступными (например, файл конфигурации или БД). Только в этом случае быстрое и грязное решение может считаться лучше, чем решение @ jbtule, из-за более низкого уровня обслуживания.

Исходное сообщение: Я нашел ответ jbtule немного сложным для быстрого и грязного защищенного строкового шифрования AES, а в ответе Бретт была ошибка с фиксированным значением вектора инициализации, что делало его уязвимым для атак заполнения, поэтому я исправил код Бретта и добавил случайный IV, который добавляется в строку с шифрованием, создавая разные зашифрованные значения для каждого шифрования одного и того же значения:

Шифрование:

public static string Encrypt(string clearText)
{            
    byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
    using (Aes encryptor = Aes.Create())
    {
        byte[] IV = new byte[15];
        rand.NextBytes(IV);
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
                cs.Close();
            }
            clearText = Convert.ToBase64String(IV) + Convert.ToBase64String(ms.ToArray());
        }
    }
    return clearText;
}

Расшифровка:

public static string Decrypt(string cipherText)
{
    byte[] IV = Convert.FromBase64String(cipherText.Substring(0, 20));
    cipherText = cipherText.Substring(20).Replace(" ", "+");
    byte[] cipherBytes = Convert.FromBase64String(cipherText);
    using (Aes encryptor = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.Close();
            }
            cipherText = Encoding.Unicode.GetString(ms.ToArray());
        }
    }
    return cipherText;
}

Замените EncryptionKey своим ключом. В моей реализации ключ сохраняется в файле конфигурации (web.config \ app.config), так как вам не следует сохранять его в жестком коде. Файл конфигурации должен быть также зашифрованный, чтобы ключ не сохранялся в нем в виде открытого текста.

protected static string _Key = "";
protected static string EncryptionKey
{
    get
    {
        if (String.IsNullOrEmpty(_Key))
        {
            _Key = ConfigurationManager.AppSettings["AESKey"].ToString();
        }

        return _Key;
    }
}

В то время как ваш метод Encrypt генерирует разные значения для каждого вызова даже с одним и тем же простым текстом, Substring(20) будет каждый раз одинаковым, верно?

dub stylee 18.03.2016 19:57

Что значит «то же самое»? Функция дешифрования берет каждое значение, сгенерированное функцией шифрования, разделяет его на вектор инициализации и зашифрованное значение и расшифровывает его,

Gil Cohen 24.03.2016 21:55

Я не заметил, что Encrypt каждый раз генерировал разные IV. По какой-то причине я думал, что IV был один и тот же каждый раз, что, по сути, делало это бессмысленным.

dub stylee 24.03.2016 22:10

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

jbtule 29.03.2017 20:50

Я понимаю, что вы говорите @jbtule, но на самом деле все дело в управлении рисками. Если все, что вам нужно сделать, это хранить некоторую конфиденциальную информацию локально и вам нужна дополнительная защита с помощью AES, это решение может удовлетворить ваши потребности.

Gil Cohen 30.03.2017 15:15

@GilCohen Хорошо, сделайте большой отказ от ответственности и скажите, что используйте только для данных в состоянии покоя, не раскрывайте с помощью службы, и тогда вы можете требовать управления рисками. тем не мение, ваш быстрый и грязный - это просто неряшливо. Например, почему вы заменяете пробелы знаками плюса при расшифровке, а не наоборот, потому что что-то еще изменяет зашифрованный текст до того, как вы его получите? Это похоже на передачу через строку запроса URL, cookie или переменную формы, хм, это звучит как услуга, что абсолютно необходимо, когда вы нужно аутентифицируете зашифрованный текст.

jbtule 30.03.2017 17:52

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

Gil Cohen 06.08.2017 15:28

Алгоритм AES:

public static class CryptographyProvider
    {
        public static string EncryptString(string plainText, out string Key)
        {
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");

            using (Aes _aesAlg = Aes.Create())
            {
                Key = Convert.ToBase64String(_aesAlg.Key);
                ICryptoTransform _encryptor = _aesAlg.CreateEncryptor(_aesAlg.Key, _aesAlg.IV);

                using (MemoryStream _memoryStream = new MemoryStream())
                {
                    _memoryStream.Write(_aesAlg.IV, 0, 16);
                    using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, _encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter _streamWriter = new StreamWriter(_cryptoStream))
                        {
                            _streamWriter.Write(plainText);
                        }
                        return Convert.ToBase64String(_memoryStream.ToArray());
                    }
                }
            }
        }
        public static string DecryptString(string cipherText, string Key)
        {

            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(Key))
                throw new ArgumentNullException("Key");

            string plaintext = null;

            byte[] _initialVector = new byte[16];
            byte[] _Key = Convert.FromBase64String(Key);
            byte[] _cipherTextBytesArray = Convert.FromBase64String(cipherText);
            byte[] _originalString = new byte[_cipherTextBytesArray.Length - 16];

            Array.Copy(_cipherTextBytesArray, 0, _initialVector, 0, _initialVector.Length);
            Array.Copy(_cipherTextBytesArray, 16, _originalString, 0, _cipherTextBytesArray.Length - 16);

            using (Aes _aesAlg = Aes.Create())
            {
                _aesAlg.Key = _Key;
                _aesAlg.IV = _initialVector;
                ICryptoTransform decryptor = _aesAlg.CreateDecryptor(_aesAlg.Key, _aesAlg.IV);

                using (MemoryStream _memoryStream = new MemoryStream(_originalString))
                {
                    using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader _streamReader = new StreamReader(_cryptoStream))
                        {
                            plaintext = _streamReader.ReadToEnd();
                        }
                    }
                }
            }
            return plaintext;
        }
    }

1) IV передается как параметр, что означает, что разработчик должен выполнять управление IV, а воля ошибается. Вместо этого IV должен генерироваться случайным образом и храниться вместе с зашифрованным текстом. 2) Поскольку IV и ключ будут меняться между несколькими выполнениями метода Encryption и не сохраняются, нет необходимости иметь этот метод вообще, кроме как для демонстрационных целей. 3) Зашифрованный текст не аутентифицируется, поэтому злоумышленники могут манипулировать им без вашего обнаружения (см .: атака оракула с заполнением).

Artjom B. 10.06.2016 11:23

хай @ArtjomB. разработчику не нужно беспокоиться об управлении iv, потому что он будет сгенерирован и добавлен вместе с зашифрованной строкой.

Skull 10.06.2016 12:56

Я не согласен. IV хранится в переменной класса _iv, а не записывается как в зашифрованный текст. Итак, как вы думаете, как получатель будет знать ключ и IV? Их нужно было бы каким-то другим способом распределять. Поскольку IV не должен быть секретным, он должен генерироваться случайным образом для каждого шифрования и распространяться вместе с зашифрованным текстом.

Artjom B. 10.06.2016 13:03
skullpsgblog.blogspot.in/2016/05/…
Skull 10.06.2016 13:11

1) В приведенной выше ссылке вы можете получить способ реализации aes без необходимости беспокоиться об управлении iv, потому что iv также зашифровывается вместе со строкой. 2) поскольку функция, на которую вы ссылаетесь, содержит модификатор частного доступа, вы не можете вызывать ее снаружи. Для шифрования мы можем использовать только функцию Cryptographyclass.Encrytion ("SAMPLEstring")

Skull 15.06.2016 15:37

Для поддержки Маттмансер ответ. Вот пример использования класса MachineKey для шифрования / дешифрования безопасных значений URL.

Следует иметь в виду, как упоминалось ранее, это будет использовать настройки конфигурации машины (https://msdn.microsoft.com/en-us/library/ff649308.aspx). Вы можете установить ключ / алгоритм шифрования и дешифрования вручную (это может понадобиться, особенно если ваш сайт работает на нескольких серверах) в файле web.config. Вы можете сгенерировать ключи из IIS (см. Здесь: https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-the-iis-manager/) или можете использовать онлайн-генератор машинных ключей, например: http://www.developerfusion.com/tools/generatemachinekey/

    private static readonly UTF8Encoding Encoder = new UTF8Encoding();

    public static string Encrypt(string unencrypted)
    {
        if (string.IsNullOrEmpty(unencrypted)) 
            return string.Empty;

        try
        {
            var encryptedBytes = MachineKey.Protect(Encoder.GetBytes(unencrypted));

            if (encryptedBytes != null && encryptedBytes.Length > 0)
                return HttpServerUtility.UrlTokenEncode(encryptedBytes);    
        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }

    public static string Decrypt(string encrypted)
    {
        if (string.IsNullOrEmpty(encrypted)) 
            return string.Empty;

        try
        {
            var bytes = HttpServerUtility.UrlTokenDecode(encrypted);
            if (bytes != null && bytes.Length > 0)
            {
                var decryptedBytes = MachineKey.Unprotect(bytes);
                if (decryptedBytes != null && decryptedBytes.Length > 0)
                    return Encoder.GetString(decryptedBytes);
            }

        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }

Шифрование - очень распространенное дело в программировании. Я думаю, что лучше установить пакет, который сделает эту задачу за вас. Может быть, простой проект Nuget с открытым исходным кодом, например Простое шифрование AES

Ключ находится в файле конфигурации, поэтому его легко изменить в производственной среде, и я не вижу никаких недостатков.

<MessageEncryption>
  <EncryptionKey KeySize = "256" Key = "3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8 = "/>
</MessageEncryption>

Большой недостаток в том, что это не аутентифицированное шифрование.

jbtule 29.03.2017 20:13

Альтернативой BouncyCastle для шифрования AES-GCM является libsodium-net. Он является оболочкой для библиотеки libsodium C. Одним из приятных преимуществ является то, что он использует расширение AES-NI в процессорах для очень быстрого шифрования. Обратной стороной является то, что он не будет работать вообще, если у ЦП нет расширения. Программного отката нет.

Со ссылкой на Зашифровать и расшифровать строку в C# я нашел одно из хороших решений:

static readonly string PasswordHash = "P@@Sw0rd";
static readonly string SaltKey = "S@LT&KEY";
static readonly string VIKey = "@1B2c3D4e5F6g7H8";

Для шифрования

public static string Encrypt(string plainText)
{
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
    var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
    var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));

    byte[] cipherTextBytes;

    using (var memoryStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            cipherTextBytes = memoryStream.ToArray();
            cryptoStream.Close();
        }
        memoryStream.Close();
    }
    return Convert.ToBase64String(cipherTextBytes);
}

Для расшифровки

public static string Decrypt(string encryptedText)
{
    byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
    byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
    var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };

    var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
    var memoryStream = new MemoryStream(cipherTextBytes);
    var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
    byte[] plainTextBytes = new byte[cipherTextBytes.Length];

    int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
    memoryStream.Close();
    cryptoStream.Close();
    return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray());
}

Жестко запрограммированные Salt и IV, и, использующие для них представление ASCII, это все неправильно.

jbtule 29.03.2017 20:43
Предупреждение безопасности: не используйте этот код See my comment above.
jbtule 09.05.2017 01:00

Прошу прощения, что не объяснил это. IV не является ключом, и сохранение его в секрете дает нулевую дополнительную безопасность, а, делая его предсказуемым, теряет немалую безопасность. Жесткое кодирование IV совершенно неразумно / нелогично / неправильно для любого, кто действительно знает, как использовать шифрование AES-CBC. Encoding.ASCII.GetBytes для данных, предназначенных для добавления энтропии к чему-то, выбранному человеком, будет намного меньше энтропии, чем ожидалось, и является ошибкой новичка очень. Это все вещи, которые легко исправить, но это не так, поэтому мое смелое предупреждение остается в силе из-за последствий для безопасности.

jbtule 09.05.2017 07:59

Рахул, успокойся! Устройтесь поудобнее, расслабьтесь и подумайте, почему все 3 комментария от @jbtule получили голоса за. Он говорит что-то разумное, чтобы направить вас на правильный путь. Не на что обижаться. Вы новичок в SO. Со временем вы поймете, как это работает.

Nikhil Vartak 25.05.2017 10:26

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

public class Program
{
    public static void Main()
    {
        var key = Encoding.UTF8.GetBytes("SUkbqO2ycDo7QwpR25kfgmC7f8CoyrZy");
        var data = Encoding.UTF8.GetBytes("testData");

        //Encrypt data
        var encrypted = CryptoHelper.EncryptData(data,key);

        //Decrypt data
        var decrypted = CryptoHelper.DecryptData(encrypted,key);

        //Display result
        Console.WriteLine(Encoding.UTF8.GetString(decrypted));
    }
}

public static class CryptoHelper
{
    public static byte[] EncryptData(byte[] data, byte[] key)
    {
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;
            using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
            {
                using (var msEncrypt = new MemoryStream())
                {
                    msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);

                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        csEncrypt.Write(data, 0, data.Length);

                    return msEncrypt.ToArray();
                }
            }
        }

    }

    public static byte[] DecryptData(byte[] encrypted, byte[] key)
    {
        var iv = new byte[16];
        Buffer.BlockCopy(encrypted, 0, iv, 0, iv.Length);
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;
            using (var decryptor = aesAlg.CreateDecryptor(key, iv))
            {
                using (var msDecrypt = new MemoryStream(encrypted, iv.Length, encrypted.Length - iv.Length))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var resultStream = new MemoryStream())
                        {
                            csDecrypt.CopyTo(resultStream);
                            return resultStream.ToArray();
                        }
                    }
                }
            }
        }
    }
}

Вот образец, как шифрование / дешифрование AES-GCM может быть выполнено с использованием пакета Bouncy Castle.

Я нашел этот образец, когда искал в Google возможность расшифровать данные из GOlang crypto/aes api:

const (
    gcmBlockSize         = 16 // this is key size
    gcmTagSize           = 16 // this is mac
    gcmStandardNonceSize = 12 // this is nonce
)

func encrypt(data []byte, passphrase string) []byte {
    block, _ := aes.NewCipher([]byte(createHash(passphrase)))
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        panic(err.Error())
    }
    nonce := make([]byte, gcm.NonceSize())
    if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
        panic(err.Error())
    }
    ciphertext := gcm.Seal(nonce, nonce, data, nil)
    return ciphertext
}

Пример .Net работает как шарм с ключом (256 бит), Mac (128 бит) и nonce (96 бит).

Следующий код представляет собой улучшенную версию отвечать Ghazal по сравнению с аналогичным вопрос.

public class EncryptionHelper
{
    private Aes aesEncryptor;

    public EncryptionHelper()
    {
    }

    private void BuildAesEncryptor(string key)
    {
        aesEncryptor = Aes.Create();
        var pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
        aesEncryptor.Key = pdb.GetBytes(32);
        aesEncryptor.IV = pdb.GetBytes(16);
    }

    public string EncryptString(string clearText, string key)
    {
        BuildAesEncryptor(key);
        var clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, aesEncryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
            }
            var encryptedText = Convert.ToBase64String(ms.ToArray());
            return encryptedText;
        }
    }

    public string DecryptString(string cipherText, string key)
    {
        BuildAesEncryptor(key);
        cipherText = cipherText.Replace(" ", "+");
        var cipherBytes = Convert.FromBase64String(cipherText);
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, aesEncryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
            }
            var clearText = Encoding.Unicode.GetString(ms.ToArray());
            return clearText;
        }
    }
}

В следующем примере показано, как зашифровать и расшифровать образцы данных:

    // This constant is used to determine the keysize of the encryption algorithm in bits.
    // We divide this by 8 within the code below to get the equivalent number of bytes.
    private const int Keysize = 128;

    // This constant determines the number of iterations for the password bytes generation function.
    private const int DerivationIterations = 1000;

    public static string Encrypt(string plainText, string passPhrase)
    {
        // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
        // so that the same Salt and IV values can be used when decrypting.  
        var saltStringBytes = GenerateBitsOfRandomEntropy(16);
        var ivStringBytes = GenerateBitsOfRandomEntropy(16);
        var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
        {
            var keyBytes = password.GetBytes(Keysize / 8);
            using (var symmetricKey = new RijndaelManaged())
            {
                symmetricKey.BlockSize = 128;
                symmetricKey.Mode = CipherMode.CBC;
                symmetricKey.Padding = PaddingMode.PKCS7;
                using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                {
                    using (var memoryStream = new MemoryStream())
                    {
                        using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
                            var cipherTextBytes = saltStringBytes;
                            cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                            cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                            memoryStream.Close();
                            cryptoStream.Close();
                            return Convert.ToBase64String(cipherTextBytes);
                        }
                    }
                }
            }
        }
    }

    public static string Decrypt(string cipherText, string passPhrase)
    {
        // Get the complete stream of bytes that represent:
        // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
        var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
        // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
        var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
        // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
        var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
        // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
        var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

        using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
        {
            var keyBytes = password.GetBytes(Keysize / 8);
            using (var symmetricKey = new RijndaelManaged())
            {
                symmetricKey.BlockSize = 128;
                symmetricKey.Mode = CipherMode.CBC;
                symmetricKey.Padding = PaddingMode.PKCS7;
                using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                {
                    using (var memoryStream = new MemoryStream(cipherTextBytes))
                    {
                        using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                        {
                            var plainTextBytes = new byte[cipherTextBytes.Length];
                            var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                            memoryStream.Close();
                            cryptoStream.Close();
                            return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                        }
                    }
                }
            }
        }
    }

    private static byte[] GenerateBitsOfRandomEntropy(int size)
    {
        // 32 Bytes will give us 256 bits.
        // 16 Bytes will give us 128 bits.
        var randomBytes = new byte[size]; 
        using (var rngCsp = new RNGCryptoServiceProvider())
        {
            // Fill the array with cryptographically secure random bytes.
            rngCsp.GetBytes(randomBytes);
        }
        return randomBytes;
    }

Спасибо, @reza .. собираешься использовать его для некоторых домашних проектов, если можно?

Arrie 02.03.2020 22:42

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Text;  

/// <summary>
/// Summary description for Encryption
/// </summary>
public class Encryption
{
    public TripleDES CreateDES(string key)
    {
        MD5 md5 = new MD5CryptoServiceProvider();
        TripleDES des = new TripleDESCryptoServiceProvider();
        des.Key = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
        des.IV = new byte[des.BlockSize / 8];
        return des;
    }
    public  byte[] Encryptiondata(string PlainText)
    {
        TripleDES des = CreateDES("DreamMLMKey");
        ICryptoTransform ct = des.CreateEncryptor();
        byte[] input = Encoding.Unicode.GetBytes(PlainText);
        return ct.TransformFinalBlock(input, 0, input.Length);
    }

    public string Decryptiondata(string CypherText)
    {
        string stringToDecrypt = CypherText.Replace(" ", "+");
        int len = stringToDecrypt.Length;
        byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 

        byte[] b = Convert.FromBase64String(CypherText);
        TripleDES des = CreateDES("DreamMLMKey");
        ICryptoTransform ct = des.CreateDecryptor();
        byte[] output = ct.TransformFinalBlock(b, 0, b.Length);
        return Encoding.Unicode.GetString(output);
    }
    public string Decryptiondataurl(string CypherText)
    {
        string newcyperttext=CypherText.Replace(' ', '+');
        byte[] b = Convert.FromBase64String(newcyperttext);
        TripleDES des = CreateDES("DreamMLMKey");
        ICryptoTransform ct = des.CreateDecryptor();
        byte[] output = ct.TransformFinalBlock(b, 0, b.Length);
        return Encoding.Unicode.GetString(output);
    }


    #region  encryption & Decription
    public  string Encrypt(string input, string key)
    {
        byte[] inputArray = UTF8Encoding.UTF8.GetBytes(input);
        TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
        tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);
        tripleDES.Mode = CipherMode.ECB;
        tripleDES.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = tripleDES.CreateEncryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
        tripleDES.Clear();
        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
    }
    public  string Decrypt(string input, string key)
    {
        byte[] inputArray = Convert.FromBase64String(input);
        TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
        tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);
        tripleDES.Mode = CipherMode.ECB;
        tripleDES.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = tripleDES.CreateDecryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
        tripleDES.Clear();
        return UTF8Encoding.UTF8.GetString(resultArray);
    }

    public string encrypt(string encryptString)
    {
        string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
                0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76
            });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                encryptString = Convert.ToBase64String(ms.ToArray());
            }
        }
        return encryptString;
    }

    public string Decrypt(string cipherText)
    {
        string EncryptionKey = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
                0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76
            });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

    #endregion
}

MD5 наименее безопасен. Не рекомендуется.

vapcguy 15.02.2020 00:13

Хороший пример, как это сделать, используя PGPCore с BouncyCastle, очень простое решение: https://blog.bitscry.com/2018/07/05/pgp-encryption-and-decryption-in-c/

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

using (PGP pgp = new PGP())
{
// Generate keys
pgp.GenerateKey(@"C:\TEMP\keys/public.asc", @"C:\TEMP\keys\private.asc", "[email protected]", "password");
// Encrypt file
pgp.EncryptFile(@"C:\TEMP\keys\content.txt", @"C:\TEMP\keys\content__encrypted.pgp", @"C:\TEMP\keys/public.asc", true, true);
// Encrypt and sign file
pgp.EncryptFileAndSign(@"C:\TEMP\keys\content.txt", @"C:\TEMP\keys\content__encrypted_signed.pgp", @"C:\TEMP\keys/public.asc", @"C:\TEMP\keys\private.asc", "password", true, true);
// Decrypt file
pgp.DecryptFile(@"C:\TEMP\keys\content__encrypted.pgp", @"C:\TEMP\keys\content__decrypted.txt", @"C:\TEMP\keys\private.asc", "password");
// Decrypt signed file
pgp.DecryptFile(@"C:\TEMP\keys\content__encrypted_signed.pgp", @"C:\TEMP\keys\content__decrypted_signed.txt", @"C:\TEMP\keys\private.asc", "password");

// Encrypt stream
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\keys\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\keys\content__encrypted2.pgp"))
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\keys/public.asc", FileMode.Open))
    pgp.EncryptStream(inputFileStream, outputFileStream, publicKeyStream, true, true);

// Decrypt stream
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\keys\content__encrypted2.pgp", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\keys\content__decrypted2.txt"))
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\keys\private.asc", FileMode.Open))
    pgp.DecryptStream(inputFileStream, outputFileStream, privateKeyStream, "password");
}

У меня есть проект с открытым исходным кодом под названием X509Крипто, который использует сертификаты для шифрования и дешифрования строк. Очень легко использовать. Вот пример того, как его использовать:

1. Используйте Интерфейс командной строки X509Crypto (CLI) для создания нового сертификата шифрования и пары ключей.

>x509crypto.exe
X509Crypto> makecert -context user -keysize medium -alias myvault

Certificate with thumbprint B31FE7E7AE5229F8186782742CF579197FA859FD was added to X509Alias "myvault" in the user X509Context

X509Crypto>

2. Используйте команду интерфейса командной строки Зашифровать, чтобы добавить секрет в ваш новый X509Alias.

X509Crypto> encrypt -text -alias myvault -context user -secret apikey -in "80EAF03248965AC2B78090"

Secret apikey has been added to X509Alias myvault in the user X509Context

X509Crypto>

3. Укажите секрет в своей программе.

После того, как вы установили X509Alias с добавленным секретом (секретами), легко восстановить их в своей программе с установленным пакетом Nuget Org.X509Crypto:

using Org.X509Crypto;

namespace SampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var Alias = new X509Alias(@"myvault", X509Context.UserReadOnly);
            var apiKey = Alias.RecoverSecret(@"apikey");
        }
    }
}

Другие вопросы по теме