Я ищу очень простую функцию обфускации (например, шифрование и дешифрование, но не обязательно безопасную) для некоторых данных. Это не критично. Мне нужно что-то, чтобы честные люди оставались честными, но что-то посильнее, чем ROT13 или Base64.
Я бы предпочел что-то, что уже включено в структуру .СЕТЬ 2.0, поэтому мне не нужно беспокоиться о каких-либо внешних зависимостях.
Я действительно не хочу возиться с общедоступными / закрытыми ключами и т. д. Я мало знаю о шифровании, но я знаю достаточно, чтобы знать, что все, что я написал, будет менее чем бесполезным ... Фактически, Я бы, наверное, напортачил с математикой и взломал ее тривиально.
Сэкономьте часы и используйте HttpServerUtility.UrlTokenEn / Decode для преобразования байтовых массивов туда и обратно в строку, удобную для URL.
+1 за то, что не пытались выкатить собственный умный дизайн. Возможно, вы мало знаете о шифровании, но тот факт, что вы знаете, ставит вас на световые годы впереди большинства разработчиков, которых я встречал, которые мало что знают о шифровании, но думают, что в любом случае могут создать свое собственное решение.
Внимание: многие ответы на этот вопрос относятся только к шифрованию без аутентификации. Это означает, что файл злоумышленник может изменить данные без уведомления приложения. Это также приводит к другим серьезным уязвимостям (например, дешифрование без ключа из-за оракула заполнения). TL; DR: Не используйте код в приведенных ответах, если вас это не устраивает или вы не понимаете, что я только что сказал.
В этом вопросе есть два условия: «безопасность не критически важна» и «отсутствие внешних зависимостей», и для большинства из них это может навредить безопасности, если скопировать и вставить эти ответы. В идеале вы хотите использовать библиотека с открытым исходным кодом высокого уровня для большей безопасности, отказ от ответственности: я перенес это на C#, чтобы он существовал.. Если это не сработает, не делайте уступок, аутентифицируйте зашифрованный текст, правильно используйте IV, например, в моем Современные примеры симметричного аутентифицированного шифрования строки C#.
@paxdiablo Похоже, это действительно касается C#, а не C. Было ли ваше редактирование ошибкой?
@CodesInChaos Почему вы закрылись, а затем снова открылись?
@KyleStrand Я не помню. Но я думаю, что пытался закрыть его как дубликат stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string вместо stackoverflow.com/questions/10168240/…, но не смог проголосовать за закрытие дважды. Это, безусловно, согласуется с тем, что я закрываю последнее как дубликат первого в то время.





Пространство имен System.Security.Cryptography содержит классы TripleDESCryptoServiceProvider и RijndaelManaged.
Не забудьте добавить ссылку на сборку System.Security.
Не то чтобы я проголосовал против, но почему возраст вопроса должен иметь значение при голосовании?
Да, добавить сборку 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, который я обсуждаю ниже.
@richdiet. Извините, я не принял ваш ответ. Другой ответ с 37+ голосами, потому что он более актуален. Спасибо за ответ, он все еще хороший.
@MarkBrittingham: любой блочный шифр без функции объединения блоков, вектора инициализации и правильного заполнения небезопасен. Использование DES - наименее важная проблема этой схемы.
Так где же ключ?
Если вам просто нужно простое шифрование (то есть, если решительный взломщик может взломать, но заблокировать большинство случайных пользователей), просто выберите две парольные фразы равной длины, например:
deoxyribonucleicacid
while (x>0) { x-- };
и xor ваших данных с обоими из них (цикл парольных фраз, если необходимо) (a). Например:
1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) {
Кто-то, просматривающий ваш двоичный файл, вполне может подумать, что строка ДНК является ключом, но они вряд ли подумают, что код C - это что-то иное, кроме неинициализированной памяти, сохраненной с вашим двоичным кодом.
(a) Имейте в виду, что это простое шифрование очень и, по некоторым определениям, может вообще не считаться шифрованием (поскольку целью шифрования является неавторизованный доступ предотвращать, а не просто усложнять его). Хотя, конечно, даже самое надежное шифрование небезопасно, когда кто-то стоит над держателями ключей стальной трубой.
Как указано в первом предложении, это средство достаточно затруднить для случайного злоумышленника, что они двинутся дальше. Это похоже на предотвращение краж в вашем доме - вам не нужно делать его неприступным, вам просто нужно сделать его менее беременным, чем соседний дом :-)
Интересная идея. Я не уверен, что «поверю» исходному коду в двоичном формате, но как насчет адаптации идеи использования сообщения об ошибке в качестве ключевой фразы?
Я предпочитаю использовать md5-хэш некоторой строки открытого текста, которая уже существует в приложении (сообщение об ошибке или около того).
Почему они должны быть одинаковой длины? На самом деле кажется лучше, если они будут разной длины. Таким образом, длина вашего эффективного операнда XOR равна LCM (length1, length2), а не только length1 (= length2). Что, конечно, становится length1 * length2, если длины относительно простые.
@jbtule, если бы вы прочитали вопрос, вы бы поняли, что более безопасное шифрование никоим образом не требуется. В частности, ссылка на «простое шифрование», «не критично» и просто «сохранение честности честных людей». Вам также следует прочитать мой первый абзац, в котором прямо говорится о том, что он не блокирует решительных злоумышленников.
@paxdiablo В вопросе также высказывается озабоченность по поводу тривиального взлома. В конечном итоге вопрос плохо задан, вероятно, вызвал много плохих ответов на вопросы безопасности, и тем не менее люди использовали ответы на этот вопрос, просто читая заголовок.
@jbtule, если кто-то читает что-то, в котором довольно четко указаны его ограничения, а затем ожидает, что это будет работать вне этих ограничений, виноват Oни. Показательный пример: я недавно отказал своему 13-летнему сыну в разрешении на использование бензопилы, потому что он считал, что ему не нужно беспокоиться о том, чтобы узнавать об отдаче и других опасностях - меня больше всего беспокоит, чтобы он и все его друзья достигли совершеннолетия со всеми четырьмя конечности целы :-) С точки зрения этого ответа, я дал очень сильно прояснить ожидаемые недостатки в первом предложении. Однако я обновлю ответ, чтобы было понятнее.
@paxdiablo, возможно, вам будет интересно взвесить это мета-обсуждение: meta.stackoverflow.com/q/348946/637783
@paxdiablo Проверьте ответы на вопрос и угадайте, сколько людей пришло, чтобы найти решение аналогичной проблемы. Они используют это небезопасное решение. Код копируется. От людей, нашедших этот ответ, и от людей, которые скопировали людей, нашедших этот ответ (и так далее).
@steffan, я не имею ни малейшего представления, ни даже самой приблизительной догадки, ни кто-либо другой, я ручаюсь. Мой комментарий все еще в силе. Если вы прочитали ответ, в котором подробно описаны все его недостатки, и решите использовать (или распространить) его ненадлежащим образом, это ваша ответственность. Это ничем не отличается от бутылочек с лекарствами, в которых говорится, что вы можете принимать только две таблетки в час. Если вы решите передозировку, проглотив целую бутылку, это вряд ли вина Glaxo. Или, ближе к дому, если я посоветую вам использовать C для написания ОС, а затем вы решите, что вам следует использовать C для все,, это также ваша вина, а не моя.
Шифрование - это просто: как отмечали другие, в пространстве имен 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/ для простого создания нового набора:
KeyVectorИспользовать это просто: просто создайте экземпляр класса, а затем вызовите (обычно) 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;
}
}
Спасибо - это один из моих любимых ответов, и я рад, что люди его используют. Некоторые люди думают, что вам следует солить начало зашифрованной строки, чтобы избежать обнаружения векторного массива, но на самом деле это зависит от того, насколько вы параноидально относитесь к тому, что кто-то пытается расшифровать ваши строки, и знают ли они, что шифруется для начала.
Полиция using (это не я) может преследовать вас за использование потока
Будет ли это гарантировать, что memoryStream закрыт? Сейчас я использую слово «использование» гораздо чаще, чем когда я писал этот алгоритм, поэтому я могу понять вашу точку зрения. Однако не хочу менять это без тестирования, так что это может занять некоторое время ...
@Chris, нет никакой пользы от Disposing of MemoryStreams - они не похожи на обычные потоки, которые содержат ресурс ОС - они просто содержат буферный объект, который будет собираться, как и все другие объекты в памяти.
@AndyMcKenna - Это сделано специально, чтобы вы меняли значения в массивах, как отмечает Марк во втором абзаце.
Паук прав. Вы ДЕЙСТВИТЕЛЬНО не должны использовать ключи, указанные в коде. Измени их на свои собственные. С "плохим" ключом у вас не будет выбора ;-)
Привет, Марк, есть ли способ сократить длину зашифрованной строки / массива байтов для данной строки?
Учита - к сожалению, нет. Большинство современных алгоритмов шифрования именно таким образом расширяют зашифрованную строку. Вы мощь пробуете алгоритм типа zip на результате. Однако я думаю, что вы обнаружите, что сжатие будет минимальным из-за характера вывода шифрования. Хотя должен признать - я не уверен в этом, потому что никогда не пробовал. Я просто припоминаю, как где-то читал, что здесь мало компрессии.
Допустим, кто-то хотел добавить к этому посол, как бы это сделать? :-п
Марк, вы не должны использовать в своем ответе такие слова, как «выше», поскольку ответы меняются по отношению друг к другу. Фактически, единственный тонкий ответ над этим ответом в порядке голосования - это вопрос.
Замечательно - обратите внимание, что вы можете использовать Convert.ToBase64String и Convert.FromBase64String, чтобы получить более компактное строковое представление в ваших методах EncryptString и DecryptString (а не в ручных методах StrToByteArray и ByteArrayToStr)
Спасибо тебе за это! Есть ли способ узнать, какой будет длина выходного байтового массива из Encrypt, на основе определенной длины входной строки?
Энди, мне сразу в голову приходят две мысли. Сначала попробуйте 4 x Длина. Возможно, это неправильно, но это пришло мне в голову, как что-то, что я обнаружил ранее. Во-вторых, попробуйте запустить несколько строк и посмотрите, существует ли постоянная связь между размером ввода и размером вывода. Вы НЕ можете просто попробовать это один раз и предположить, что это будет актуально для всех. Действительно, отношения могут сильно отличаться по содержанию.
Марк, спасибо за ответ. Я попробовал то, что вы предложили, на 9 миллионах случайно сгенерированных строк длиной от 10 до 16 символов. 15–16 символов всегда возвращают 32 байта. При уменьшении до 11 символов 32 байта были возвращены только несколько раз, и, наконец, с 10 символами все 9-метровые шифрование вернули 16 байтов. Судя только по этому вашему выводу, количество байтов зависит от содержимого. В моих строках всегда будет 10 символов, поэтому на всякий случай я предположил, что длина в любом случае может быть 32 байта (это упражнение заключалось в определении длины поля базы данных varbinary). Еще раз спасибо!
Преобразование в базу 64, а затем кодирование URL приведет к меньшему количеству кода и более компактному выводу.
Этот код использует RijndaelManaged. Код эквивалентен 128 (или выше) AES?
К вашему сведению: небольшая ошибка компилятора в строке 11, где вы использовали число выше 255 в векторном массиве байтов.
@DomenicDatti - это не ошибка. Если вы прочтете комментарии, вы увидите, что я сделал это специально, чтобы люди не просто использовали значения ключей, показанные в примере.
@AntonAndreev см. blogs.msdn.com/b/shawnfa/archive/2006/10/09/… для взаимодействия - однако теперь, когда класс Aes находится в .net 3.5+, почему бы просто не использовать это, то есть System.Security.Cryptography.AesManaged? :)
Вы не должны использовать IV таким образом. Для данных двух сообщений они не должны были быть зашифрованы одним и тем же ключом и одним и тем же IV. IV должен быть случайным для каждого сообщения, добавляться к криптопотоку и считываться перед расшифровкой. crypto.stackexchange.com/a/82/1934
@jbtule - Вот почему я включил функцию «GenerateEncryptionVector», чтобы вы могли делать это в среде, требующей максимальной безопасности. Однако в большинстве коммерческих настроек шифрования это немного излишне. Конечно, с течением времени то, что имеет тенденцию быть более экзотичным с точки зрения безопасности, становится стандартом из-за простой гонки вооружений между разработчиками и теми, кто будет эксплуатировать информационные системы.
Использование случайного IV для каждого сообщения не экзотично или ново, а просто важно и является частью конструкции алгоритма. Использование предсказуемого IV для каждого сообщения - распространенная криптографическая ошибка, которую не нужно повторять.
Сделал слепую копию и вставил и тоже обрадовался, увидев, что он работает после того, как столкнулся с адом «Ошибка при декодировании заполнения OAEP».
Обратите внимание, что этот код использует цепочку блоков шифрования (CBC) в качестве режима, который вы должны принять во внимание.
Статическим функциям GenerateEncryptionKey и GenerateEncryptionVector не нужно вызывать GenerateKey / GenerateIV, поскольку это делается в конструкторе класса RijndaelManaged.
Также обратите внимание, что следствием использования CBC в качестве режима является то, что вы, вероятно, будете уязвимы для дополнения атак оракула. Используйте аутентифицированное шифрование и, по возможности, не внедряйте криптографию самостоятельно.
Просто интересно, есть ли причина, по которой вы (и все остальные, ответившие в этом потоке) проходите через все искажения использования MemoryStream и CryptoStream вместо использования метода ICryptoTransform.TransformFinalBlock (), как показано в этой программе: zenu.wordpress.com/2011/09/21/…? Если есть причина избегать этого, я бы обязательно об этом услышал.
Не используйте постоянный IV. Пожалуйста, не говорите людям об этом, вы нарушаете функцию безопасности алгоритма шифрования. Нет причин, по которым вы не можете сгенерировать новый IV для каждого шифрования и добавить его в свой криптотекст.
"Все, что вам нужно сделать, это изменить некоторые числа (должно быть <= 255) в массивах Key и Vector (я оставил одно недопустимое значение в массиве Vector, чтобы убедиться, что вы это делаете ...). Вы можете использовать random.org/bytes легко создать новый набор ". Это совсем не хороший совет. Измените немного номеров?
Обратите внимание, что в базе 64 используется на ~ 33% больше символов, чем байтов. Текущий метод использует на 200% больше символов.
Я использую этот алгоритм как есть, но при интенсивном использовании я заметил некоторые проблемы. 100% строк, которые я зашифровываю, верны, но иногда без всякой причины, если я расшифровываю и повторно шифрую данные, происходит молчаливый сбой. Я получаю зашифрованное значение, которое невозможно расшифровать. это произошло 10 раз за 200000 итераций. Не катастрофично, но это вызывает у нас проблемы. Кто-нибудь еще экспериментировал с этим?
Янник: Я не сталкивался с какими-либо проблемами при использовании алгоритма, но, опять же, с частотой отказов 10/200000 я не уверен, что поймал бы его. Поскольку это, по сути, просто пакет методов, предлагаемых MS, я полагаю, что это будет что-то с их стороны.
Я нашел проблему. Метод шифрования и дешифрования не является потокобезопасным. Я добавил код, чтобы сделать его потокобезопасным и больше никаких ошибок. Для дальнейших ссылок, вот ошибка, которую я имел: Вероятное состояние гонки ввода-вывода обнаружено при копировании памяти. Пакет ввода-вывода по умолчанию не является потокобезопасным. В многопоточных приложениях доступ к потоку должен осуществляться потокобезопасным способом, например, потокобезопасная оболочка, возвращаемая TextReader или TextWriter.
IV должен быть разным каждый раз 1) так что, если вы зашифруете одно и то же сообщение несколько раз, это дало бы вам разные результаты (аналогично соли) 2) потому что некоторые алгоритмы катастрофически терпят неудачу, если вы когда-либо повторно используете IV
Почему здесь так много комментариев о том, что ответ ненадежен? Не каждое шифрование должно быть очень безопасным. OP запросил простое небезопасное шифрование. Возможно, безопасность не важна. Достаточно только того факта, что текст искажен и не закодирован в Base64.
[EDIT] Спустя годы я вернулся, чтобы сказать: не делай этого! Подробнее см. Что не так с шифрованием XOR?.
Очень простым и легким двусторонним шифрованием является шифрование XOR.
mypass.mypassmypassmypass...) mypassmypassmypass...) .@Ryan Не в каждой ситуации требуются криптографически безопасные хэши или шифры Rijndael. «Простое двустороннее шифрование» на самом деле может означать просто, что предполагает xor или даже ROT13.
@Ryan: AES со статическим ключом шифрования, без вектора инициализации и без функции цепочки блоков - это просто причудливое название для шифрования XOR, вы просто используете действительно причудливый KDF ...
@jbtule Вопрос специально задан для алгоритма insecure. Шифрование XOR идеально подходит из-за своей простоты. Я думаю, что это правильный ответ - просто выберите достаточно длинный случайный пароль.
Я изменил это:
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 ('', '+'));
Никогда не используйте постоянный вектор инициализации, см .: crypto.stackexchange.com/questions/66/… для получения дополнительной информации о том, почему. Вместо этого сгенерируйте новый IV для каждого шифрования и добавьте его в криптотекст, что намного лучше и проще.
EncryptToUrl и DecryptFromUrl не имеют необходимого бизнеса в этом классе. Иначе ; отличный ответ.
Имейте в виду, что вывод метода EncryptToUrl в этом решении (или любое использование строки UrlEncoded base 64 в целом) не будет работать по умолчанию в IIS 7 при использовании как часть пути URL (а не строки запроса), как в маршрут ASP.NET MVC из-за настройки безопасности IIS 7. Подробнее см .: stackoverflow.com/a/2014121/12484
Как и 40-Love, мне тоже пришлось заменить символы в моей строке шифра. Строка поступала из браузера QueryString, я не совсем уверен, что с ней не так, но вызов Replace ('', '+')); before I Decrypt исправил это для меня. #
@TomHeard Как это можно сделать с помощью приведенного выше кода?
Вариант ответа Марка (отлично)
Надеюсь это поможет
[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
Никогда не используйте постоянный вектор инициализации, см .: crypto.stackexchange.com/questions/66/… для получения дополнительной информации о том, почему. Вместо этого сгенерируйте новый IV для каждого шифрования и добавьте его в криптотекст, что намного лучше и проще.
@TomHeard - как это сделать на C#?
@Chalky При шифровании вы используете класс Rijndael для генерации случайного IV для вас (msdn.microsoft.com/en-us/library/…), выполняете шифрование, а затем получаете IV из экземпляра Rijndael, используя свойство IV. Затем вы добавляете (или добавляете, работает до тех пор, пока ваша дешифровка захватывает его с той же стороны) его к своему криптографическому тексту. Затем при расшифровке вы извлекаете IV из полученных данных (размер свойства IV такой же, как свойство BlockSize, деленное на 8), а затем передаете его вашему экземпляру расшифровки перед расшифровкой.
@Chalky Обратите внимание, что IV не обязательно должен быть секретным, он просто должен быть уникальным для каждого отправленного сообщения.
Просто подумал, что я бы добавил, что я улучшил 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) Это полностью защищено от атак с выбранным зашифрованным текстом (в частности, оракулов заполнения).
@jbtule, пожалуйста, не вводите в заблуждение каждого человека, который не хочет, чтобы осложнения просто шифровались, а также тех, кто не опасается атаки, - Пожалуйста, не заказывайте, если вы хотите давать предложения.
@Virbhadrasinh, я не ошибаюсь, на самом деле все наоборот. Если вы собираетесь использовать AES, очень важно использовать его правильно, неправильно использовать и говорить, что все в порядке, я не использую его для чего-то важного - это заблуждение.
@jbtule Если этот код уязвим для какой-либо атаки - а это так, - тогда, возможно, вместо того, чтобы просто кричать на нас, чтобы мы не использовали его, вы могли бы ссылаться на какой-то код, который делает это правильно. Просто говорю.
@Corey Не кричит и следовал лучшим методам решения проблем безопасности в ответах на переполнение стека. Если вам нужна ссылка, она была размещена в комментариях к вопросу. Но я тоже положу сюда для тебя stackoverflow.com/a/10366194/637783
Я объединил то, что нашел лучшее из нескольких ответов и комментариев.
Код:
/// <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);
IV добавляется в виде открытого текста, как рекомендовано @Tom Heard и другими выше. Это те 16 байтов, на которые вы ссылаетесь. Если вы зашифруете IV, Decrypt не сможет извлечь IV.
В этом случае: byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();
Это будет делать то же самое, что и текущая реализация, не так ли?
"ЗАПОЛНИТЕ МЕНЯ 16, 24 ИЛИ 32 СИМВОЛАМИ" ну нет, не декодирование до base 64. И ключ должен быть случайным. Действительно случайно.
Я заметил, что @bpsilver прав, и предоставленный код не будет работать без его исправления: метод encrypt возвращает зашифрованные данные без IV (он сначала добавляет IV во входной буфер, но затем шифрует и возвращает данные без него). Так что, если возможно, просто обновите ответ его кодом. (Примечание: я тестирую только методы с параметрами byte [], а не строки). Спасибо!
Я думаю, это самый простой в мире!
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 в качестве примера того, что он не сделал хочет сделать.
Я хотел опубликовать свое решение, поскольку ни одно из вышеперечисленных решений не является таким простым, как мое. Дайте мне знать, что вы думаете:
// 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.
В этом примере с использованием встроенной библиотеки криптографии .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 заимствованы из здесь):
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
Привет, Марк - без проблем. Мне было жаль, что мне пришлось отказаться от ответа от richdiet, поскольку я действительно использовал его решение, и оно сработало отлично. Однако я продолжал возвращаться сюда, чтобы прочитать другие ответы, и ваш действительно лучше. Нет причин говорить людям использовать что-то, что, хотя и работает, на самом деле не лучший способ сделать что-то, когда доступен лучший ответ.