Простое небезопасное двустороннее "запутывание" данных?

Я ищу очень простую функцию обфускации (например, шифрование и дешифрование, но не обязательно безопасную) для некоторых данных. Это не критично. Мне нужно что-то, чтобы честные люди оставались честными, но что-то посильнее, чем ROT13 или Base64.

Я бы предпочел что-то, что уже включено в структуру .СЕТЬ 2.0, поэтому мне не нужно беспокоиться о каких-либо внешних зависимостях.

Я действительно не хочу возиться с общедоступными / закрытыми ключами и т. д. Я мало знаю о шифровании, но я знаю достаточно, чтобы знать, что все, что я написал, будет менее чем бесполезным ... Фактически, Я бы, наверное, напортачил с математикой и взломал ее тривиально.

Привет, Марк - без проблем. Мне было жаль, что мне пришлось отказаться от ответа от richdiet, поскольку я действительно использовал его решение, и оно сработало отлично. Однако я продолжал возвращаться сюда, чтобы прочитать другие ответы, и ваш действительно лучше. Нет причин говорить людям использовать что-то, что, хотя и работает, на самом деле не лучший способ сделать что-то, когда доступен лучший ответ.

Matt Dawdy 07.02.2010 06:53

Сэкономьте часы и используйте HttpServerUtility.UrlTokenEn / Decode для преобразования байтовых массивов туда и обратно в строку, удобную для URL.

Praesagus 13.02.2010 03:25

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

Dinah 11.03.2010 21:26

Внимание: многие ответы на этот вопрос относятся только к шифрованию без аутентификации. Это означает, что файл злоумышленник может изменить данные без уведомления приложения. Это также приводит к другим серьезным уязвимостям (например, дешифрование без ключа из-за оракула заполнения). TL; DR: Не используйте код в приведенных ответах, если вас это не устраивает или вы не понимаете, что я только что сказал.

usr 26.01.2013 20:51

В этом вопросе есть два условия: «безопасность не критически важна» и «отсутствие внешних зависимостей», и для большинства из них это может навредить безопасности, если скопировать и вставить эти ответы. В идеале вы хотите использовать библиотека с открытым исходным кодом высокого уровня для большей безопасности, отказ от ответственности: я перенес это на C#, чтобы он существовал.. Если это не сработает, не делайте уступок, аутентифицируйте зашифрованный текст, правильно используйте IV, например, в моем Современные примеры симметричного аутентифицированного шифрования строки C#.

jbtule 20.02.2013 17:54
Ни один ответ на этот вопрос не описывает безопасное шифрование. Use jbtule's answer at Зашифровать и расшифровать строку instead.
CodesInChaos 20.05.2016 18:34

@paxdiablo Похоже, это действительно касается C#, а не C. Было ли ваше редактирование ошибкой?

Kyle Strand 10.05.2017 22:14

@CodesInChaos Почему вы закрылись, а затем снова открылись?

Kyle Strand 10.05.2017 22:15

@KyleStrand Я не помню. Но я думаю, что пытался закрыть его как дубликат stackoverflow.com/questions/202011/encrypt-and-decrypt-a-str‌ing вместо stackoverflow.com/questions/10168240/…, но не смог проголосовать за закрытие дважды. Это, безусловно, согласуется с тем, что я закрываю последнее как дубликат первого в то время.

CodesInChaos 10.05.2017 22:23
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
433
10
220 660
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

Пространство имен System.Security.Cryptography содержит классы TripleDESCryptoServiceProvider и RijndaelManaged.

Не забудьте добавить ссылку на сборку System.Security.

Не то чтобы я проголосовал против, но почему возраст вопроса должен иметь значение при голосовании?

user247702 24.04.2014 15:39

Да, добавить сборку System.Security, импортировать пространство имен System.Security.Cryptography. Вот простой пример шифрования с использованием симметричного (DES) алгоритма:

DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.GenerateKey();
byte[] key = des.Key; // save this!

ICryptoTransform encryptor = des.CreateEncryptor();
// encrypt
byte[] enc = encryptor.TransformFinalBlock(new byte[] { 1, 2, 3, 4 }, 0, 4);

ICryptoTransform decryptor = des.CreateDecryptor();

// decrypt
byte[] originalAgain = decryptor.TransformFinalBlock(enc, 0, enc.Length);
Debug.Assert(originalAgain[0] == 1);

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

Mark Brittingham 12.12.2008 16:04

@richdiet. Извините, я не принял ваш ответ. Другой ответ с 37+ голосами, потому что он более актуален. Спасибо за ответ, он все еще хороший.

Matt Dawdy 07.02.2010 02:11

@MarkBrittingham: любой блочный шифр без функции объединения блоков, вектора инициализации и правильного заполнения небезопасен. Использование DES - наименее важная проблема этой схемы.

Hubert Kario 19.08.2012 05:00

Так где же ключ?

Alex 31.10.2013 17:19
Предупреждение безопасности: не используйте этот код See comment by @HubertKario
jbtule 09.05.2017 01:20

Если вам просто нужно простое шифрование (то есть, если решительный взломщик может взломать, но заблокировать большинство случайных пользователей), просто выберите две парольные фразы равной длины, например:

deoxyribonucleicacid
while (x>0) { x-- };

и xor ваших данных с обоими из них (цикл парольных фраз, если необходимо) (a). Например:

1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) { 

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


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

Как указано в первом предложении, это средство достаточно затруднить для случайного злоумышленника, что они двинутся дальше. Это похоже на предотвращение краж в вашем доме - вам не нужно делать его неприступным, вам просто нужно сделать его менее беременным, чем соседний дом :-)

Интересная идея. Я не уверен, что «поверю» исходному коду в двоичном формате, но как насчет адаптации идеи использования сообщения об ошибке в качестве ключевой фразы?

Jon Skeet 03.10.2008 10:11

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

Treb 03.10.2008 11:43

Почему они должны быть одинаковой длины? На самом деле кажется лучше, если они будут разной длины. Таким образом, длина вашего эффективного операнда XOR равна LCM (length1, length2), а не только length1 (= length2). Что, конечно, становится length1 * length2, если длины относительно простые.

Fantius 27.10.2011 23:42

@jbtule, если бы вы прочитали вопрос, вы бы поняли, что более безопасное шифрование никоим образом не требуется. В частности, ссылка на «простое шифрование», «не критично» и просто «сохранение честности честных людей». Вам также следует прочитать мой первый абзац, в котором прямо говорится о том, что он не блокирует решительных злоумышленников.

paxdiablo 09.05.2017 03:06

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

jbtule 09.05.2017 03:10

@jbtule, если кто-то читает что-то, в котором довольно четко указаны его ограничения, а затем ожидает, что это будет работать вне этих ограничений, виноват Oни. Показательный пример: я недавно отказал своему 13-летнему сыну в разрешении на использование бензопилы, потому что он считал, что ему не нужно беспокоиться о том, чтобы узнавать об отдаче и других опасностях - меня больше всего беспокоит, чтобы он и все его друзья достигли совершеннолетия со всеми четырьмя конечности целы :-) С точки зрения этого ответа, я дал очень сильно прояснить ожидаемые недостатки в первом предложении. Однако я обновлю ответ, чтобы было понятнее.

paxdiablo 09.05.2017 03:46

@paxdiablo, возможно, вам будет интересно взвесить это мета-обсуждение: meta.stackoverflow.com/q/348946/637783

jbtule 09.05.2017 22:10

@paxdiablo Проверьте ответы на вопрос и угадайте, сколько людей пришло, чтобы найти решение аналогичной проблемы. Они используют это небезопасное решение. Код копируется. От людей, нашедших этот ответ, и от людей, которые скопировали людей, нашедших этот ответ (и так далее).

steffen 11.05.2017 20:08

@steffan, я не имею ни малейшего представления, ни даже самой приблизительной догадки, ни кто-либо другой, я ручаюсь. Мой комментарий все еще в силе. Если вы прочитали ответ, в котором подробно описаны все его недостатки, и решите использовать (или распространить) его ненадлежащим образом, это ваша ответственность. Это ничем не отличается от бутылочек с лекарствами, в которых говорится, что вы можете принимать только две таблетки в час. Если вы решите передозировку, проглотив целую бутылку, это вряд ли вина Glaxo. Или, ближе к дому, если я посоветую вам использовать C для написания ОС, а затем вы решите, что вам следует использовать C для все,, это также ваша вина, а не моя.

paxdiablo 12.05.2017 03:34

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

Шифрование - это просто: как отмечали другие, в пространстве имен System.Security.Cryptography есть классы, которые делают всю работу за вас. Используйте их, а не какой-либо домашний раствор.

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

Я бы использовал одно из следующих решений:

  • DPAPI с использованием класса ProtectedData с областью действия CurrentUser. Это просто, потому что вам не нужно беспокоиться о ключе. Данные могут быть расшифрованы только одним и тем же пользователем, поэтому не подходит для обмена данными между пользователями или машинами.

  • DPAPI с использованием класса ProtectedData с областью действия LocalMachine. Подходит, например, для защита данных конфигурации на едином защищенном сервере. Но любой, кто может войти в систему, может его зашифровать, так что ничего хорошего, если сервер не защищен.

  • Любой симметричный алгоритм. Я обычно использую статический метод SymmetricAlgorithm.Create (), если мне все равно, какой алгоритм используется (на самом деле по умолчанию это Rijndael). В этом случае вам нужно как-то защитить свой ключ. Например. вы можете каким-то образом запутать его и скрыть в своем коде. Но имейте в виду, что любой, кто достаточно умен, чтобы декомпилировать ваш код, скорее всего, сможет найти ключ.

Ответ принят как подходящий

Другие ответы здесь работают нормально, но AES - более безопасный и современный алгоритм шифрования. Это класс, который я получил несколько лет назад для выполнения шифрования AES, который я со временем модифицировал, чтобы он был более удобным для веб-приложений (например, я создал методы шифрования / дешифрования, которые работают с дружественной к URL-адресам строкой). Также в нем есть методы, работающие с байтовыми массивами.

ПРИМЕЧАНИЕ: вы должны использовать разные значения в массивах Key (32 байта) и Vector (16 байтов)! Вы бы не хотели, чтобы кто-то выяснил ваши ключи, просто предположив, что вы использовали этот код как есть! Все, что вам нужно сделать, это изменить некоторые числа (должно быть <= 255) в массивах Key и Vector (я оставил одно недопустимое значение в массиве Vector, чтобы убедиться, что вы это делаете ...). Вы можете использовать https://www.random.org/bytes/ для простого создания нового набора:

Использовать это просто: просто создайте экземпляр класса, а затем вызовите (обычно) EncryptToString (строка StringToEncrypt) и DecryptString (строка StringToDecrypt) в качестве методов. Нет ничего проще (или безопаснее), если у вас есть этот класс.


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


public class SimpleAES
{
    // Change these keys
    private byte[] Key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private byte[] Vector = __Replace_Me__({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 2521, 112, 79, 32, 114, 156 });


    private ICryptoTransform EncryptorTransform, DecryptorTransform;
    private System.Text.UTF8Encoding UTFEncoder;

    public SimpleAES()
    {
        //This is our encryption method
        RijndaelManaged rm = new RijndaelManaged();

        //Create an encryptor and a decryptor using our encryption method, key, and vector.
        EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
        DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

        //Used to translate bytes to text and vice versa
        UTFEncoder = new System.Text.UTF8Encoding();
    }

    /// -------------- Two Utility Methods (not used but may be useful) -----------
    /// Generates an encryption key.
    static public byte[] GenerateEncryptionKey()
    {
        //Generate a Key.
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateKey();
        return rm.Key;
    }

    /// Generates a unique encryption vector
    static public byte[] GenerateEncryptionVector()
    {
        //Generate a Vector
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateIV();
        return rm.IV;
    }


    /// ----------- The commonly used methods ------------------------------    
    /// Encrypt some text and return a string suitable for passing in a URL.
    public string EncryptToString(string TextValue)
    {
        return ByteArrToString(Encrypt(TextValue));
    }

    /// Encrypt some text and return an encrypted byte array.
    public byte[] Encrypt(string TextValue)
    {
        //Translates our text value into a byte array.
        Byte[] bytes = UTFEncoder.GetBytes(TextValue);

        //Used to stream the data in and out of the CryptoStream.
        MemoryStream memoryStream = new MemoryStream();

        /*
         * We will have to write the unencrypted bytes to the stream,
         * then read the encrypted result back from the stream.
         */
        #region Write the decrypted value to the encryption stream
        CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
        cs.Write(bytes, 0, bytes.Length);
        cs.FlushFinalBlock();
        #endregion

        #region Read encrypted value back out of the stream
        memoryStream.Position = 0;
        byte[] encrypted = new byte[memoryStream.Length];
        memoryStream.Read(encrypted, 0, encrypted.Length);
        #endregion

        //Clean up.
        cs.Close();
        memoryStream.Close();

        return encrypted;
    }

    /// The other side: Decryption methods
    public string DecryptString(string EncryptedString)
    {
        return Decrypt(StrToByteArray(EncryptedString));
    }

    /// Decryption when working with byte arrays.    
    public string Decrypt(byte[] EncryptedValue)
    {
        #region Write the encrypted value to the decryption stream
        MemoryStream encryptedStream = new MemoryStream();
        CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
        decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
        decryptStream.FlushFinalBlock();
        #endregion

        #region Read the decrypted value from the stream.
        encryptedStream.Position = 0;
        Byte[] decryptedBytes = new Byte[encryptedStream.Length];
        encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
        encryptedStream.Close();
        #endregion
        return UTFEncoder.GetString(decryptedBytes);
    }

    /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
    //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
    //      return encoding.GetBytes(str);
    // However, this results in character values that cannot be passed in a URL.  So, instead, I just
    // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
    public byte[] StrToByteArray(string str)
    {
        if (str.Length == 0)
            throw new Exception("Invalid string value in StrToByteArray");

        byte val;
        byte[] byteArr = new byte[str.Length / 3];
        int i = 0;
        int j = 0;
        do
        {
            val = byte.Parse(str.Substring(i, 3));
            byteArr[j++] = val;
            i += 3;
        }
        while (i < str.Length);
        return byteArr;
    }

    // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction:
    //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    //      return enc.GetString(byteArr);    
    public string ByteArrToString(byte[] byteArr)
    {
        byte val;
        string tempStr = "";
        for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
        {
            val = byteArr[i];
            if (val < (byte)10)
                tempStr += "00" + val.ToString();
            else if (val < (byte)100)
                tempStr += "0" + val.ToString();
            else
                tempStr += val.ToString();
        }
        return tempStr;
    }
}

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

Mark Brittingham 21.05.2009 01:37

Полиция using (это не я) может преследовать вас за использование потока

Chris S 24.09.2009 01:45

Будет ли это гарантировать, что memoryStream закрыт? Сейчас я использую слово «использование» гораздо чаще, чем когда я писал этот алгоритм, поэтому я могу понять вашу точку зрения. Однако не хочу менять это без тестирования, так что это может занять некоторое время ...

Mark Brittingham 24.09.2009 14:03

@Chris, нет никакой пользы от Disposing of MemoryStreams - они не похожи на обычные потоки, которые содержат ресурс ОС - они просто содержат буферный объект, который будет собираться, как и все другие объекты в памяти.

Frank Krueger 12.11.2009 08:22

@AndyMcKenna - Это сделано специально, чтобы вы меняли значения в массивах, как отмечает Марк во втором абзаце.

Pauk 02.12.2009 15:27

Паук прав. Вы ДЕЙСТВИТЕЛЬНО не должны использовать ключи, указанные в коде. Измени их на свои собственные. С "плохим" ключом у вас не будет выбора ;-)

Mark Brittingham 02.12.2009 16:29

Привет, Марк, есть ли способ сократить длину зашифрованной строки / массива байтов для данной строки?

Uchitha 03.12.2009 13:36

Учита - к сожалению, нет. Большинство современных алгоритмов шифрования именно таким образом расширяют зашифрованную строку. Вы мощь пробуете алгоритм типа zip на результате. Однако я думаю, что вы обнаружите, что сжатие будет минимальным из-за характера вывода шифрования. Хотя должен признать - я не уверен в этом, потому что никогда не пробовал. Я просто припоминаю, как где-то читал, что здесь мало компрессии.

Mark Brittingham 06.12.2009 20:49

Допустим, кто-то хотел добавить к этому посол, как бы это сделать? :-п

Hungry Beast 14.03.2010 23:16

Марк, вы не должны использовать в своем ответе такие слова, как «выше», поскольку ответы меняются по отношению друг к другу. Фактически, единственный тонкий ответ над этим ответом в порядке голосования - это вопрос.

paxdiablo 13.08.2010 13:39

Замечательно - обратите внимание, что вы можете использовать Convert.ToBase64String и Convert.FromBase64String, чтобы получить более компактное строковое представление в ваших методах EncryptString и DecryptString (а не в ручных методах StrToByteArray и ByteArrayToStr)

zcrar70 11.09.2010 01:46

Спасибо тебе за это! Есть ли способ узнать, какой будет длина выходного байтового массива из Encrypt, на основе определенной длины входной строки?

Andreas Ågren 13.01.2011 15:33

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

Mark Brittingham 14.01.2011 17:54

Марк, спасибо за ответ. Я попробовал то, что вы предложили, на 9 миллионах случайно сгенерированных строк длиной от 10 до 16 символов. 15–16 символов всегда возвращают 32 байта. При уменьшении до 11 символов 32 байта были возвращены только несколько раз, и, наконец, с 10 символами все 9-метровые шифрование вернули 16 байтов. Судя только по этому вашему выводу, количество байтов зависит от содержимого. В моих строках всегда будет 10 символов, поэтому на всякий случай я предположил, что длина в любом случае может быть 32 байта (это упражнение заключалось в определении длины поля базы данных varbinary). Еще раз спасибо!

Andreas Ågren 19.01.2011 15:27

Преобразование в базу 64, а затем кодирование URL приведет к меньшему количеству кода и более компактному выводу.

Mud 01.04.2011 03:07

Этот код использует RijndaelManaged. Код эквивалентен 128 (или выше) AES?

Anton Andreev 04.10.2011 17:56

К вашему сведению: небольшая ошибка компилятора в строке 11, где вы использовали число выше 255 в векторном массиве байтов.

DomenicDatti 10.10.2011 22:39

@DomenicDatti - это не ошибка. Если вы прочтете комментарии, вы увидите, что я сделал это специально, чтобы люди не просто использовали значения ключей, показанные в примере.

Mark Brittingham 15.10.2011 06:22

@AntonAndreev см. blogs.msdn.com/b/shawnfa/archive/2006/10/09/… для взаимодействия - однако теперь, когда класс Aes находится в .net 3.5+, почему бы просто не использовать это, то есть System.Security.Cryptography.AesManaged? :)

Adam Tuliper - MSFT 24.01.2012 01:39

Вы не должны использовать IV таким образом. Для данных двух сообщений они не должны были быть зашифрованы одним и тем же ключом и одним и тем же IV. IV должен быть случайным для каждого сообщения, добавляться к криптопотоку и считываться перед расшифровкой. crypto.stackexchange.com/a/82/1934

jbtule 27.04.2012 17:39

@jbtule - Вот почему я включил функцию «GenerateEncryptionVector», чтобы вы могли делать это в среде, требующей максимальной безопасности. Однако в большинстве коммерческих настроек шифрования это немного излишне. Конечно, с течением времени то, что имеет тенденцию быть более экзотичным с точки зрения безопасности, становится стандартом из-за простой гонки вооружений между разработчиками и теми, кто будет эксплуатировать информационные системы.

Mark Brittingham 27.04.2012 19:47

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

jbtule 27.04.2012 20:08

Сделал слепую копию и вставил и тоже обрадовался, увидев, что он работает после того, как столкнулся с адом «Ошибка при декодировании заполнения OAEP».

prabhakaran 07.05.2012 14:37

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

Polynomial 25.07.2012 18:54

Статическим функциям GenerateEncryptionKey и GenerateEncryptionVector не нужно вызывать GenerateKey / GenerateIV, поскольку это делается в конструкторе класса RijndaelManaged.

Michel van Engelen 21.08.2012 11:49

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

Stephen Touset 16.01.2013 03:37

Просто интересно, есть ли причина, по которой вы (и все остальные, ответившие в этом потоке) проходите через все искажения использования MemoryStream и CryptoStream вместо использования метода ICryptoTransform.TransformFinalBlock (), как показано в этой программе: zenu.wordpress.com/2011/09/21/…? Если есть причина избегать этого, я бы обязательно об этом услышал.

RenniePet 05.09.2013 07:11

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

Tom Heard 03.12.2013 03:55

"Все, что вам нужно сделать, это изменить некоторые числа (должно быть <= 255) в массивах Key и Vector (я оставил одно недопустимое значение в массиве Vector, чтобы убедиться, что вы это делаете ...). Вы можете использовать random.org/bytes легко создать новый набор ". Это совсем не хороший совет. Измените немного номеров?

Maarten Bodewes 10.04.2015 01:49

Обратите внимание, что в базе 64 используется на ~ 33% больше символов, чем байтов. Текущий метод использует на 200% больше символов.

Maarten Bodewes 10.04.2015 02:00

Я использую этот алгоритм как есть, но при интенсивном использовании я заметил некоторые проблемы. 100% строк, которые я зашифровываю, верны, но иногда без всякой причины, если я расшифровываю и повторно шифрую данные, происходит молчаливый сбой. Я получаю зашифрованное значение, которое невозможно расшифровать. это произошло 10 раз за 200000 итераций. Не катастрофично, но это вызывает у нас проблемы. Кто-нибудь еще экспериментировал с этим?

Yannick Richard 20.11.2015 18:10

Янник: Я не сталкивался с какими-либо проблемами при использовании алгоритма, но, опять же, с частотой отказов 10/200000 я не уверен, что поймал бы его. Поскольку это, по сути, просто пакет методов, предлагаемых MS, я полагаю, что это будет что-то с их стороны.

Mark Brittingham 25.11.2015 22:44

Я нашел проблему. Метод шифрования и дешифрования не является потокобезопасным. Я добавил код, чтобы сделать его потокобезопасным и больше никаких ошибок. Для дальнейших ссылок, вот ошибка, которую я имел: Вероятное состояние гонки ввода-вывода обнаружено при копировании памяти. Пакет ввода-вывода по умолчанию не является потокобезопасным. В многопоточных приложениях доступ к потоку должен осуществляться потокобезопасным способом, например, потокобезопасная оболочка, возвращаемая TextReader или TextWriter.

Yannick Richard 26.11.2015 17:12

IV должен быть разным каждый раз 1) так что, если вы зашифруете одно и то же сообщение несколько раз, это дало бы вам разные результаты (аналогично соли) 2) потому что некоторые алгоритмы катастрофически терпят неудачу, если вы когда-либо повторно используете IV

Mark Sowul 19.12.2016 22:10
Предупреждение безопасности: не используйте этот код Despite being the accepted answer, there are severe security issues mention in the above comments that the author has continued to ignore for 8 years.
jbtule 09.05.2017 00:52

Почему здесь так много комментариев о том, что ответ ненадежен? Не каждое шифрование должно быть очень безопасным. OP запросил простое небезопасное шифрование. Возможно, безопасность не важна. Достаточно только того факта, что текст искажен и не закодирован в Base64.

Tony_Henrich 22.09.2017 01:30

[EDIT] Спустя годы я вернулся, чтобы сказать: не делай этого! Подробнее см. Что не так с шифрованием XOR?.

Очень простым и легким двусторонним шифрованием является шифрование XOR.

  1. Придумайте пароль. Пусть будет mypass.
  2. Преобразуйте пароль в двоичный (согласно ASCII). Пароль становится 01101101 01111001 01110000 01100001 01110011 01110011.
    .
  3. Возьмите сообщение, которое хотите закодировать. Преобразуйте это также в двоичный файл.
  4. Посмотрите на длину сообщения. Если длина сообщения составляет 400 байтов, превратите пароль в строку из 400 байтов, повторяя ее снова и снова. Это будет 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 ... (или mypassmypassmypass...)
  5. XOR сообщение с длинным паролем.
  6. Отправьте результат.
  7. В другой раз выполните XOR зашифрованного сообщения с тем же паролем (mypassmypassmypass...) .
  8. Вот твое сообщение!

@Ryan Не в каждой ситуации требуются криптографически безопасные хэши или шифры Rijndael. «Простое двустороннее шифрование» на самом деле может означать просто, что предполагает xor или даже ROT13.

user1228 12.11.2010 16:49

@Ryan: AES со статическим ключом шифрования, без вектора инициализации и без функции цепочки блоков - это просто причудливое название для шифрования XOR, вы просто используете действительно причудливый KDF ...

Hubert Kario 19.08.2012 05:02
Предупреждение безопасности: не используйте этот код XOR Encryption with a repeating key is trivially cracked.
jbtule 09.05.2017 01:07

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

Bip901 19.01.2021 15:44

Я изменил это:

public string ByteArrToString(byte[] byteArr)
{
    byte val;
    string tempStr = "";
    for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
    {
        val = byteArr[i];
        if (val < (byte)10)
            tempStr += "00" + val.ToString();
        else if (val < (byte)100)
            tempStr += "0" + val.ToString();
        else
            tempStr += val.ToString();
    }
    return tempStr;
}

к этому:

    public string ByteArrToString(byte[] byteArr)
    {
        string temp = "";
        foreach (byte b in byteArr)
            temp += b.ToString().PadLeft(3, '0');
        return temp;
    }

Я очистил SimpleAES (см. Выше) для использования. Исправлены запутанные методы шифрования / дешифрования; отдельные методы для кодирования байтовых буферов, строк и строк, удобных для URL; использовал существующие библиотеки для кодирования URL.

Код меньше, проще, быстрее, а вывод более лаконичен. Например, [email protected] производит:

SimpleAES: "096114178117140150104121138042115022037019164188092040214235183167012211175176167001017163166152"
SimplerAES: "YHKydYyWaHmKKnMWJROkvFwo1uu3pwzTr7CnARGjppg%3d"

Код:

public class SimplerAES
{
    private static byte[] key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private static byte[] vector = __Replace_Me_({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 });

    private ICryptoTransform encryptor, decryptor;
    private UTF8Encoding encoder;

    public SimplerAES()
    {
        RijndaelManaged rm = new RijndaelManaged();
        encryptor = rm.CreateEncryptor(key, vector);
        decryptor = rm.CreateDecryptor(key, vector);
        encoder = new UTF8Encoding();
    }

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

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

    public byte[] Encrypt(byte[] buffer)
    {
        return Transform(buffer, encryptor);
    }

    public byte[] Decrypt(byte[] buffer)
    {
        return Transform(buffer, decryptor);
    }

    protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {
        MemoryStream stream = new MemoryStream();
        using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        return stream.ToArray();
    }
}

При декодировании мне пришлось заменить пробел на +, чтобы он работал с QueryString в Chrome: (new SimplerAES ()). Decrypt (Request.QueryString ["myParam"]. Replace‌ ('', '+'));

live-love 30.10.2013 22:56

Никогда не используйте постоянный вектор инициализации, см .: crypto.stackexchange.com/questions/66/… для получения дополнительной информации о том, почему. Вместо этого сгенерируйте новый IV для каждого шифрования и добавьте его в криптотекст, что намного лучше и проще.

Tom Heard 03.12.2013 06:17

EncryptToUrl и DecryptFromUrl не имеют необходимого бизнеса в этом классе. Иначе ; отличный ответ.

Timothy Groote 08.01.2014 13:00

Имейте в виду, что вывод метода EncryptToUrl в этом решении (или любое использование строки UrlEncoded base 64 в целом) не будет работать по умолчанию в IIS 7 при использовании как часть пути URL (а не строки запроса), как в маршрут ASP.NET MVC из-за настройки безопасности IIS 7. Подробнее см .: stackoverflow.com/a/2014121/12484

Jon Schneider 01.03.2014 00:18

Как и 40-Love, мне тоже пришлось заменить символы в моей строке шифра. Строка поступала из браузера QueryString, я не совсем уверен, что с ней не так, но вызов Replace ('', '+')); before I Decrypt исправил это для меня. #

learnerplates 09.04.2015 15:42

@TomHeard Как это можно сделать с помощью приведенного выше кода?

MKII 27.07.2015 11:46
Предупреждение безопасности: не используйте этот код See comment by @TomHeard
jbtule 09.05.2017 00:54

Вариант ответа Марка (отлично)

  • Добавить "using" s
  • Сделайте класс IDisposable
  • Удалите код кодировки URL, чтобы упростить пример.
  • Добавьте простой тестовый инструмент, чтобы продемонстрировать использование

Надеюсь это поможет

[TestFixture]
public class RijndaelHelperTests
{
    [Test]
    public void UseCase()
    {
        //These two values should not be hard coded in your code.
        byte[] key = {251, 9, 67, 117, 237, 158, 138, 150, 255, 97, 103, 128, 183, 65, 76, 161, 7, 79, 244, 225, 146, 180, 51, 123, 118, 167, 45, 10, 184, 181, 202, 190};
        byte[] vector = {214, 11, 221, 108, 210, 71, 14, 15, 151, 57, 241, 174, 177, 142, 115, 137};

        using (var rijndaelHelper = new RijndaelHelper(key, vector))
        {
            var encrypt = rijndaelHelper.Encrypt("StringToEncrypt");
            var decrypt = rijndaelHelper.Decrypt(encrypt);
            Assert.AreEqual("StringToEncrypt", decrypt);
        }
    }
}

public class RijndaelHelper : IDisposable
{
    Rijndael rijndael;
    UTF8Encoding encoding;

    public RijndaelHelper(byte[] key, byte[] vector)
    {
        encoding = new UTF8Encoding();
        rijndael = Rijndael.Create();
        rijndael.Key = key;
        rijndael.IV = vector;
    }

    public byte[] Encrypt(string valueToEncrypt)
    {
        var bytes = encoding.GetBytes(valueToEncrypt);
        using (var encryptor = rijndael.CreateEncryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
        {
            crypto.Write(bytes, 0, bytes.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var encrypted = new byte[stream.Length];
            stream.Read(encrypted, 0, encrypted.Length);
            return encrypted;
        }
    }

    public string Decrypt(byte[] encryptedValue)
    {
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
        {
            crypto.Write(encryptedValue, 0, encryptedValue.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var decryptedBytes = new Byte[stream.Length];
            stream.Read(decryptedBytes, 0, decryptedBytes.Length);
            return encoding.GetString(decryptedBytes);
        }
    }

    public void Dispose()
    {
        if (rijndael != null)
        {
            rijndael.Dispose();
        }
    }
}

Хороший ответ. Одна вещь в методе Dispose вам нужно будет преобразовать rijndael в IDisposable, иначе вы получите ошибку уровня защиты, вызвав Dispose

John ClearZ 26.08.2013 01:23

Никогда не используйте постоянный вектор инициализации, см .: crypto.stackexchange.com/questions/66/… для получения дополнительной информации о том, почему. Вместо этого сгенерируйте новый IV для каждого шифрования и добавьте его в криптотекст, что намного лучше и проще.

Tom Heard 03.12.2013 06:20

@TomHeard - как это сделать на C#?

Chalky 18.09.2014 10:15

@Chalky При шифровании вы используете класс Rijndael для генерации случайного IV для вас (msdn.microsoft.com/en-us/library/…), выполняете шифрование, а затем получаете IV из экземпляра Rijndael, используя свойство IV. Затем вы добавляете (или добавляете, работает до тех пор, пока ваша дешифровка захватывает его с той же стороны) его к своему криптографическому тексту. Затем при расшифровке вы извлекаете IV из полученных данных (размер свойства IV такой же, как свойство BlockSize, деленное на 8), а затем передаете его вашему экземпляру расшифровки перед расшифровкой.

Tom Heard 19.09.2014 01:05

@Chalky Обратите внимание, что IV не обязательно должен быть секретным, он просто должен быть уникальным для каждого отправленного сообщения.

Tom Heard 19.09.2014 01:07
Предупреждение безопасности: не используйте этот код See above Comments by @TomHeard
jbtule 09.05.2017 00:57

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

public class StringEncryption
{
    private readonly Random random;
    private readonly byte[] key;
    private readonly RijndaelManaged rm;
    private readonly UTF8Encoding encoder;

    public StringEncryption()
    {
        this.random = new Random();
        this.rm = new RijndaelManaged();
        this.encoder = new UTF8Encoding();
        this.key = Convert.FromBase64String("Your+Secret+Static+Encryption+Key+Goes+Here = ");
    }

    public string Encrypt(string unencrypted)
    {
        var vector = new byte[16];
        this.random.NextBytes(vector);
        var cryptogram = vector.Concat(this.Encrypt(this.encoder.GetBytes(unencrypted), vector));
        return Convert.ToBase64String(cryptogram.ToArray());
    }

    public string Decrypt(string encrypted)
    {
        var cryptogram = Convert.FromBase64String(encrypted);
        if (cryptogram.Length < 17)
        {
            throw new ArgumentException("Not a valid encrypted string", "encrypted");
        }

        var vector = cryptogram.Take(16).ToArray();
        var buffer = cryptogram.Skip(16).ToArray();
        return this.encoder.GetString(this.Decrypt(buffer, vector));
    }

    private byte[] Encrypt(byte[] buffer, byte[] vector)
    {
        var encryptor = this.rm.CreateEncryptor(this.key, vector);
        return this.Transform(buffer, encryptor);
    }

    private byte[] Decrypt(byte[] buffer, byte[] vector)
    {
        var decryptor = this.rm.CreateDecryptor(this.key, vector);
        return this.Transform(buffer, decryptor);
    }

    private byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {
        var stream = new MemoryStream();
        using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }

        return stream.ToArray();
    }
}

И бонусный юнит-тест

[Test]
public void EncryptDecrypt()
{
    // Arrange
    var subject = new StringEncryption();
    var originalString = "Testing123!£$";

    // Act
    var encryptedString1 = subject.Encrypt(originalString);
    var encryptedString2 = subject.Encrypt(originalString);
    var decryptedString1 = subject.Decrypt(encryptedString1);
    var decryptedString2 = subject.Decrypt(encryptedString2);

    // Assert
    Assert.AreEqual(originalString, decryptedString1, "Decrypted string should match original string");
    Assert.AreEqual(originalString, decryptedString2, "Decrypted string should match original string");
    Assert.AreNotEqual(originalString, encryptedString1, "Encrypted string should not match original string");
    Assert.AreNotEqual(encryptedString1, encryptedString2, "String should never be encrypted the same twice");
}

1) Не используйте System.Random в качестве ГСЧ. 2) Это полностью защищено от атак с выбранным зашифрованным текстом (в частности, оракулов заполнения).

CodesInChaos 20.05.2016 18:24
Предупреждение безопасности: не используйте этот код see above comment by @CodesInChaos
jbtule 09.05.2017 00:55

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

Virbhadrasinh 23.06.2017 17:12

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

jbtule 23.06.2017 17:35

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

Corey 11.08.2017 02:44

@Corey Не кричит и следовал лучшим методам решения проблем безопасности в ответах на переполнение стека. Если вам нужна ссылка, она была размещена в комментариях к вопросу. Но я тоже положу сюда для тебя stackoverflow.com/a/10366194/637783

jbtule 11.08.2017 02:59

Я объединил то, что нашел лучшее из нескольких ответов и комментариев.

  • К зашифрованному тексту добавлен случайный вектор инициализации (@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 http://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 bytes long.
    /// </summary>
    private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 24 (2 pad chars), 32 OR 44 (1 pad char) RANDOM CHARS"); // Base64 has a blowup of four-thirds (33%)

    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 = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); 
        return _rijndael.IV.Concat(inputBuffer).ToArray();
    }
}

Обновление 2015-07-18: Исправлена ​​ошибка в приватном методе Encrypt () комментариями @bpsilver и @Evereq. IV был случайно зашифрован, теперь он добавлен в виде открытого текста, как и ожидалось Decrypt ().

Вы должны зашифровать весь inputBuffer с добавлением IV, иначе первые 16 символов строки для шифрования будут потеряны. Итак, ваш код должен читать: return _encryptor.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);

bpsilver 17.11.2014 21:04

IV добавляется в виде открытого текста, как рекомендовано @Tom Heard и другими выше. Это те 16 байтов, на которые вы ссылаетесь. Если вы зашифруете IV, Decrypt не сможет извлечь IV.

angularsen 17.11.2014 21:51

В этом случае: byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();

bpsilver 18.11.2014 00:24

Это будет делать то же самое, что и текущая реализация, не так ли?

angularsen 18.11.2014 02:13

"ЗАПОЛНИТЕ МЕНЯ 16, 24 ИЛИ 32 СИМВОЛАМИ" ну нет, не декодирование до base 64. И ключ должен быть случайным. Действительно случайно.

Maarten Bodewes 10.04.2015 01:58

Я заметил, что @bpsilver прав, и предоставленный код не будет работать без его исправления: метод encrypt возвращает зашифрованные данные без IV (он сначала добавляет IV во входной буфер, но затем шифрует и возвращает данные без него). Так что, если возможно, просто обновите ответ его кодом. (Примечание: я тестирую только методы с параметрами byte [], а не строки). Спасибо!

Evereq 18.07.2015 20:15

Я думаю, это самый простой в мире!

string encrypted = "Text".Aggregate("", (c, a) => c + (char) (a + 2));

Тестовое задание

 Console.WriteLine(("Hello").Aggregate("", (c, a) => c + (char) (a + 1)));
            //Output is Ifmmp
 Console.WriteLine(("Ifmmp").Aggregate("", (c, a) => c + (char)(a - 1)));
            //Output is Hello

РОТ ... 1? Действительно? OP даже вызвал ROT13 в качестве примера того, что он не сделал хочет сделать.

user812786 10.05.2017 21:36

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

 // This will return an encrypted string based on the unencrypted parameter
 public static string Encrypt(this string DecryptedValue)
 {
      HttpServerUtility.UrlTokenEncode(MachineKey.Protect(Encoding.UTF8.GetBytes(DecryptedValue.Trim())));
 }

 // This will return an unencrypted string based on the parameter
 public static string Decrypt(this string EncryptedValue)
 {
      Encoding.UTF8.GetString(MachineKey.Unprotect(HttpServerUtility.UrlTokenDecode(EncryptedValue)));
 }

По желанию

Это предполагает, что MachineKey сервера, используемого для шифрования значения, совпадает с ключом, используемым для дешифрования значения. При желании вы можете указать статический MachineKey в Web.config, чтобы ваше приложение могло расшифровать / зашифровать данные независимо от того, где оно запущено (например, сервер разработки или производственный сервер). Вы можете сгенерируйте статический машинный ключ, следуя этим инструкциям.

обратите внимание, что этот подход можно использовать только для приложения ASP.NET.

Feru 08.03.2017 23:54

В этом примере с использованием встроенной библиотеки криптографии .Net показано, как использовать Advanced Encryption Standard (AES).

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

namespace Aes_Example
{
    class AesExample
    {
        public static void Main()
        {
            try
            {

                string original = "Here is some data to encrypt!";

                // Create a new instance of the Aes
                // class.  This generates a new key and initialization 
                // vector (IV).
                using (Aes myAes = Aes.Create())
                {

                    // Encrypt the string to an array of bytes.
                    byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

                    // Decrypt the bytes to a string.
                    string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);

                    //Display the original data and the decrypted data.
                    Console.WriteLine("Original:   {0}", original);
                    Console.WriteLine("Round Trip: {0}", roundtrip);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key,byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");
            byte[] encrypted;
            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

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

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }


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

        }

        static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");

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

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

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

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    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();
                        }
                    }
                }

            }

            return plaintext;

        }
    }
}

Я использовал принятый ответ Марк Бриттингем, и он мне очень помог. Недавно мне пришлось отправить зашифрованный текст в другую организацию, и тут возникли некоторые проблемы. OP не требует этих параметров, но поскольку это популярный вопрос, я публикую свою модификацию (функции Encrypt и Decrypt заимствованы из здесь):

  1. Разный IV для каждого сообщения - объединяет байты IV с байтами шифра перед получением шестнадцатеричного. Конечно, это соглашение, которое необходимо передать сторонам, получающим зашифрованный текст.
  2. Позволяет использовать два конструктора - один для значений RijndaelManaged по умолчанию, а второй, где могут быть указаны значения свойств (на основе взаимного соглашения между сторонами, выполняющими шифрование и дешифрование)

Вот класс (тестовый образец в конце):

/// <summary>
/// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
/// Uses UTF8 Encoding
///  http://security.stackexchange.com/a/90850
/// </summary>
public class AnotherAES : IDisposable
{
    private RijndaelManaged rijn;

    /// <summary>
    /// Initialize algo with key, block size, key size, padding mode and cipher mode to be known.
    /// </summary>
    /// <param name = "key">ASCII key to be used for encryption or decryption</param>
    /// <param name = "blockSize">block size to use for AES algorithm. 128, 192 or 256 bits</param>
    /// <param name = "keySize">key length to use for AES algorithm. 128, 192, or 256 bits</param>
    /// <param name = "paddingMode"></param>
    /// <param name = "cipherMode"></param>
    public AnotherAES(string key, int blockSize, int keySize, PaddingMode paddingMode, CipherMode cipherMode)
    {
        rijn = new RijndaelManaged();
        rijn.Key = Encoding.UTF8.GetBytes(key);
        rijn.BlockSize = blockSize;
        rijn.KeySize = keySize;
        rijn.Padding = paddingMode;
        rijn.Mode = cipherMode;
    }

    /// <summary>
    /// Initialize algo just with key
    /// Defaults for RijndaelManaged class: 
    /// Block Size: 256 bits (32 bytes)
    /// Key Size: 128 bits (16 bytes)
    /// Padding Mode: PKCS7
    /// Cipher Mode: CBC
    /// </summary>
    /// <param name = "key"></param>
    public AnotherAES(string key)
    {
        rijn = new RijndaelManaged();
        byte[] keyArray = Encoding.UTF8.GetBytes(key);
        rijn.Key = keyArray;
    }

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// Encrypt a string using RijndaelManaged encryptor.
    /// </summary>
    /// <param name = "plainText">string to be encrypted</param>
    /// <param name = "IV">initialization vector to be used by crypto algorithm</param>
    /// <returns></returns>
    public byte[] Encrypt(string plainText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");
        byte[] encrypted;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform encryptor = rijn.CreateEncryptor(rijn.Key, IV))
        {
            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }
        // Return the encrypted bytes from the memory stream.
        return encrypted;
    }//end EncryptStringToBytes

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// </summary>
    /// <param name = "cipherText">bytes to be decrypted back to plaintext</param>
    /// <param name = "IV">initialization vector used to encrypt the bytes</param>
    /// <returns></returns>
    public string Decrypt(byte[] cipherText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");

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

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijn.Key, IV))
        {
            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                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();
                    }
                }
            }
        }

        return plaintext;
    }//end DecryptStringFromBytes

    /// <summary>
    /// Generates a unique encryption vector using RijndaelManaged.GenerateIV() method
    /// </summary>
    /// <returns></returns>
    public byte[] GenerateEncryptionVector()
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        //Generate a Vector
        rijn.GenerateIV();
        return rijn.IV;
    }//end GenerateEncryptionVector


    /// <summary>
    /// Based on https://stackoverflow.com/a/1344255
    /// Generate a unique string given number of bytes required.
    /// This string can be used as IV. IV byte size should be equal to cipher-block byte size. 
    /// Allows seeing IV in plaintext so it can be passed along a url or some message.
    /// </summary>
    /// <param name = "numBytes"></param>
    /// <returns></returns>
    public static string GetUniqueString(int numBytes)
    {
        char[] chars = new char[62];
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
        byte[] data = new byte[1];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            data = new byte[numBytes];
            crypto.GetBytes(data);
        }
        StringBuilder result = new StringBuilder(numBytes);
        foreach (byte b in data)
        {
            result.Append(chars[b % (chars.Length)]);
        }
        return result.ToString();
    }//end GetUniqueKey()

    /// <summary>
    /// Converts a string to byte array. Useful when converting back hex string which was originally formed from bytes.
    /// </summary>
    /// <param name = "hex"></param>
    /// <returns></returns>
    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }//end StringToByteArray

    /// <summary>
    /// Dispose RijndaelManaged object initialized in the constructor
    /// </summary>
    public void Dispose()
    {
        if (rijn != null)
            rijn.Dispose();
    }//end Dispose()
}//end class

и..

Вот тестовый образец:

class Program
{
    string key;
    static void Main(string[] args)
    {
        Program p = new Program();

        //get 16 byte key (just demo - typically you will have a predetermined key)
        p.key = AnotherAES.GetUniqueString(16);

        string plainText = "Hello World!";

        //encrypt
        string hex = p.Encrypt(plainText);

        //decrypt
        string roundTrip = p.Decrypt(hex);

        Console.WriteLine("Round Trip: {0}", roundTrip);
    }

    string Encrypt(string plainText)
    {
        Console.WriteLine("\nSending (encrypt side)...");
        Console.WriteLine("Plain Text: {0}", plainText);
        Console.WriteLine("Key: {0}", key);
        string hex = string.Empty;
        string ivString = AnotherAES.GetUniqueString(16);
        Console.WriteLine("IV: {0}", ivString);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //encrypting side
            byte[] IV = Encoding.UTF8.GetBytes(ivString);

            //get encrypted bytes (IV bytes prepended to cipher bytes)
            byte[] encryptedBytes = aes.Encrypt(plainText, IV);
            byte[] encryptedBytesWithIV = IV.Concat(encryptedBytes).ToArray();

            //get hex string to send with url
            //this hex has both IV and ciphertext
            hex = BitConverter.ToString(encryptedBytesWithIV).Replace("-", "");
            Console.WriteLine("sending hex: {0}", hex);
        }

        return hex;
    }

    string Decrypt(string hex)
    {
        Console.WriteLine("\nReceiving (decrypt side)...");
        Console.WriteLine("received hex: {0}", hex);
        string roundTrip = string.Empty;
        Console.WriteLine("Key " + key);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //get bytes from url
            byte[] encryptedBytesWithIV = AnotherAES.StringToByteArray(hex);

            byte[] IV = encryptedBytesWithIV.Take(16).ToArray();

            Console.WriteLine("IV: {0}", System.Text.Encoding.Default.GetString(IV));

            byte[] cipher = encryptedBytesWithIV.Skip(16).ToArray();

            roundTrip = aes.Decrypt(cipher, IV);
        }
        return roundTrip;
    }
}

Использование TripleDESCryptoServiceProvider в System.Security.Cryptography:

public static class CryptoHelper
{
    private const string Key = "MyHashString";
    private static TripleDESCryptoServiceProvider GetCryproProvider()
    {
        var md5 = new MD5CryptoServiceProvider();
        var key = md5.ComputeHash(Encoding.UTF8.GetBytes(Key));
        return new TripleDESCryptoServiceProvider() { Key = key, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
    }

    public static string Encrypt(string plainString)
    {
        var data = Encoding.UTF8.GetBytes(plainString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateEncryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Convert.ToBase64String(resultsByteArray);
    }

    public static string Decrypt(string encryptedString)
    {
        var data = Convert.FromBase64String(encryptedString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateDecryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Encoding.UTF8.GetString(resultsByteArray);
    }
}

Мне нравится это решение, но хранить ключи в классе как свойство нехорошо. Итак, я хотел бы рассказать о тех, кому это небезразлично. gist.github.com/shukebeta/780df627a3de55c848a800cfb242a276

shukebeta 09.12.2020 23:15

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