Проста незахищена двостороння “затуманення” даних?


426

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

Я вважаю за краще щось, що вже включено до .NET Framework 2.0, тому мені не потрібно турбуватися про будь-які зовнішні залежності.

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


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

3
Економте собі години та використовуйте HttpServerUtility.UrlTokenEn / Decode для перетворення назад і назад з масивів байтів у зручну для URL-адреси рядок.
Praesagus

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

6
Увага: Багато відповідей у ​​цьому питанні мають лише несанкціоноване шифрування. Це означає, що зловмисник може змінювати дані, не помічаючи програми . Це призводить до інших серйозних вразливих ситуацій (наприклад, розшифрування без ключа через oracle padding). TL; DR: Не використовуйте код у наведених відповідях, якщо з вами це не в порядку або не розумієте, що я щойно сказав.
usr

36
Жодна відповідь на це питання не описує безпечне шифрування. Використовуйте відповідь jbtule на Encrypt і замість цього розшифруйте рядок .
CodesInChaos

Відповіді:


471

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

ПРИМІТКА. Ви повинні використовувати різні значення в масивах Key (32 байти) та Vector (16 байт)! Ви не хочете, щоб хтось з'ясував ваші ключі, просто припустивши, що ви використовували цей код таким, який є! Все, що вам потрібно зробити, - це змінити деякі числа (повинні бути <= 255) в масивах Key and 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;
    }
}

53
@AndyMcKenna - це робиться спеціально, щоб ви змінювали значення в масивах, як зазначає Марк у другому абзаці.
Паук

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

30
Використання випадкового ІV для кожного повідомлення не є екзотичним чи новим, просто важливим та частиною конструкції алгоритму. Використання передбачуваного ІV для кожного повідомлення - це поширена криптовалюта, яку не потрібно продовжувати.
jbtule

14
Зауважте також, що наслідком використання CBC як його режиму є те, що ви, ймовірно, будете вразливі до нападу оракул . Використовуйте автентифіковане шифрування і, коли це можливо, не застосовуйте криптографію самостійно .
Стівен Тусет

57
Попередження про безпеку: не користуйтеся цим кодексом. Незважаючи на прийняту відповідь, у вищезазначених коментарях є серйозні проблеми безпеки, які автор продовжує ігнорувати протягом 8 років.
jbtule

176

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

Код невеликий, простіший, швидший і вихід більш стислий. Наприклад, johnsmith@gmail.comвиробляє:

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();
    }
}

2
Під час декодування мені довелося замінити простір на +, щоб він працював з QueryString в Chrome: (новий SimplerAES ()). Розшифрувати (Request.QueryString ["myParam"]. Замінити ('', '+'));
live-love

20
Ніколи не використовуйте постійний вектор ініціалізації, див.: Crypto.stackexchange.com/questions/66/… для отримання додаткової інформації про те, чому. Натомість генеруйте новий IV для кожного шифрування та додайте його до криптотексту, так що краще та не важко.
Том Херд

2
Майте на увазі, що вихід методу EncryptToUrl у цьому рішенні (або взагалі будь-яке використання рядка 64 UrlEncoded base 64) не працюватиме за замовчуванням під IIS 7, коли використовується як частина шляху до URL (не рядок запиту), як у маршрут ASP.NET MVC через налаштування безпеки IIS 7. Детальніше дивіться: stackoverflow.com/a/2014121/12484
Джон Шнайдер

5
@TomHeard Як би ви зробили це з вищевказаним кодом?
MKII

26
Попередження безпеки: не використовуйте цей код Дивіться коментар @TomHeard
jbtule

36

Так, додайте 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);

5
Це приємне, компактне двостороннє шифрування. Єдине застереження полягає в тому, що DES більше не вважається найсучаснішою безпекою. Ця назва зараз належить до алгоритму AES, про який я розповідаю нижче.
Марк Бріттінгем

@richdiet. Вибачте, що я не прийняв вашу відповідь. Інша відповідь - 37+ голосів, оскільки вона є більш актуальною. Дякую за вашу відповідь, оскільки це все ще хороша.
Метт Дауді

14
@MarkBrittingham: будь-який блок-шифр без функції блокування ланцюга, вектор ініціалізації та належне набивання не є безпечним. Використання DES є найменш важливою проблемою цієї схеми.
Хуберт Каріо

2
Тож де використовується ключ?
Олексій

22
Попередження безпеки: не використовуйте цей код Дивіться коментар @HubertKario
jbtule

28

Просто подумав, я додам, що я вдосконалив грязькі SimplerAES, додавши випадковий 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");
}

11
1) Не використовуйте System.Randomяк RNG. 2) Це вкрай порушено на атаки вибраних шифротекстів (зокрема, оракул)
CodesInChaos

21
Попередження про безпеку: не використовуйте цей код. Див. Вище коментар від @CodesInChaos
jbtule

@jbtule, будь ласка, не помиляйтесь з усіма людьми, які не хочуть, щоб ускладнення просто шифрувалися, а також хто не насторожено ставився до нападу, - Будь ласка, не замовляйте, якщо хочете дати пропозицію.
Virbhadrasinh

@Virbhadrasinh з мого боку немає помилок, насправді це зовсім навпаки. Якщо ви збираєтеся використовувати AES, правильне його використання є досить важливим, неправильне використання, і сказати, що це нормально, я не використовую його для чогось важливого, неправильно.
jbtule

1
@Corey Не кричав і дотримувався найкращої практики для вирішення проблем безпеки у відповідях на переповнення стека. Якщо ви хочете посилання, воно було розміщене в коментарях до запитання. Але я покладу його тут для вас, а stackoverflow.com/a/10366194/637783
jbtule

12

Варіант відповіді Маркса (відмінна)

  • Додати "за допомогою" s
  • Зробіть клас ідентифікаційним
  • Видаліть код кодування 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();
        }
    }
}

Хороша відповідь. Одне з методів розпорядження вам потрібно буде передати Rijndael на IDisposable або ви отримаєте помилку рівня захисту, зателефонувавши Dispose
John ClearZ

8
Ніколи не використовуйте постійний вектор ініціалізації, див.: Crypto.stackexchange.com/questions/66/… для отримання додаткової інформації про те, чому. Натомість генеруйте новий IV для кожного шифрування та додайте його до криптотексту, так що краще та не важко.
Том Херд

5
@Chalky При шифруванні ви використовуєте клас Rijndael, щоб генерувати випадковий IV для вас ( msdn.microsoft.com/en-us/library/… ), зробіть ваше шифрування, а потім захопіть IV з екземпляра Rijndael за допомогою властивості IV . Потім ви додаєте (або додаєте, або працює, доки ваш розшифровувач захопить його з тієї ж сторони) до криптотексту. Після розшифрування ви витягуєте IV з отриманих даних (Розмір властивості IV такий же, як властивість BlockSize, розділений на 8), після чого передаєте його вашому екземпляру розшифрування перед розшифруванням.
Том Херд

2
@Chalky Зауважте, що IV не повинен бути секретним, він повинен бути унікальним для кожного надісланого повідомлення.
Том Херд

20
Попередження про безпеку: не використовуйте цей код. Див. Вище Коментарі @TomHeard
jbtule

8

[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. Там ваше повідомлення!

10
@Ryan Не кожна ситуація вимагає криптографічно захищених хешей або шифрів Rijndael. "Просте двостороннє шифрування" насправді може означати просте , що говорить про xor або навіть ROT13.

1
@Ryan: AES зі статичним ключем шифрування, відсутністю вектора ініціалізації та функцією блокування ланцюга не є просто фантазійною назвою для шифрування XOR, ви просто використовуєте дійсно фантазійний KDF ...
Hubert Kario

17
Попередження про безпеку: не використовуйте цей код XOR Шифрування за допомогою повторюваного ключа тривіально зламається.
jbtule

7

Я поєднав те, що знайшов найкраще, з кількох відповідей та коментарів.

  • Випадковий вектор ініціалізації, передбачений криптотекстом (@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

2
У такому випадку:byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();
bpsilver

1
Це зробило б те саме, що і поточна реалізація, чи не так?
angularsen

1
"Заповніть мене 16, 24 АБО 32 ЧАРАМ" ну, ні, не до розшифровки бази 64. І ключ повинен бути випадковим. Дійсно випадковий.
Maarten Bodewes

1
Я зауважую, що @bpsilver правильно, і наданий код не буде працювати без його виправлення: метод шифрування повертає зашифровані дані без IV (він спочатку додає IV до inputbuffer, але наступне шифрування та повернення даних без нього). Тож, якщо можливо, просто оновіть відповідь своїм кодом. (Примітка. Я перевіряю лише методи з параметрами байт [], а не рядками). Дякую!
Evereq

6

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

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

і поповніть свої дані обома ними (циклічно пропустіть фрази) (a) . Наприклад:

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

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


(a) Майте на увазі, що це дуже просте шифрування, і, за деякими визначеннями, воно взагалі не може вважатися шифруванням (оскільки метою шифрування є запобігання несанкціонованому доступу, а не просто ускладнювати його). Хоча, звичайно, навіть найсильніше шифрування небезпечно, коли хтось стоїть над держателями ключів зі сталевою трубою.

Як сказано в першому реченні, це засіб, щоб зробити його досить важким для випадкового нападника, що вони рухатимуться далі. Це схоже на запобігання крадіжкам у вашому будинку - вам не потрібно робити його неприступним, просто потрібно зробити його менш витривалим, ніж сусідній будинок :-)


3
Цікава ідея. Я не впевнений, що я б "повірив" вихідний код у двійковий код, але як щодо адаптації ідеї використовувати повідомлення про помилку як пароль?
Джон Скіт

1
Я вважаю за краще використовувати хеш md5 деякої рядки ясного тексту, яка вже існує в додатку (повідомлення про помилку чи так).
Треб

2
Чому вони повинні бути однакової довжини? Насправді здається краще, якщо вони різної довжини. Таким чином, довжина вашого ефективного операнда XOR - LCM (length1, length2), а не просто length1 (= length2). Що, звичайно, стає length1 * length2, якщо довжини є відносно простими.
Fantius

15
Попередження про безпеку: Не використовуйте цей код Повторюваний ключ XOR легко зламається, лише за допомогою деяких загальних знань про зашифровані дані.
jbtule

3
@jbtule, якби ви прочитали питання, зрозуміли б, що більш безпечне шифрування ні в якому разі не потрібно. Зокрема, посилання на "просте шифрування", "не на критичну місію", а просто на "чесність людей". Ви також повинні прочитати мій перший абзац, який прямо зазначає той факт, що він не блокує рішучих нападників.
paxdiablo

5

Шифрування просте: як уже вказували інші, в просторі імен System.Security.Cryptography є класи, які виконують всю роботу за вас. Використовуйте їх, а не будь-яке домашнє рішення.

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

Я б застосував одне з наступних рішень:

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

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

  • Будь-який симетричний алгоритм. Зазвичай я використовую статичний метод SymmetricAlgorithm.Create (), якщо мені все одно, який алгоритм використовується (адже за замовчуванням це Rijndael). У цьому випадку вам потрібно якось захистити свій ключ. Наприклад, ви можете їх притупити якимсь чином і заховати у своєму коді. Але майте на увазі, що той, хто достатньо розумний для декомпіляції вашого коду, швидше за все, зможе знайти ключ.


5

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

 // 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
Феру

2

Простір імен System.Security.Cryptographyмістить TripleDESCryptoServiceProviderіRijndaelManaged класи класи

Не забудьте додати посилання на System.Securityзбірку.


8
Не те, щоб я виступав проти, але чому повинен бути вік питання при голосуванні?
user247702

2

Використання 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);
    }
}

1

Я змінив це :

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;
    }

1

Використовуючи вбудовану бібліотеку криптографії .Net, у цьому прикладі показано, як використовувати Розширений стандарт шифрування (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;

        }
    }
}

0

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


0

Я використовував прийняту відповідь Марка Бріттінгема, і це мені дуже допомогло. Нещодавно мені довелося надіслати зашифрований текст іншій організації, і ось тут виникли деякі проблеми. ОП не вимагає цих варіантів, але оскільки це популярне питання, я публікую свої зміни ( 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;
    }
}

введіть тут опис зображення


-2

Я думаю, це світ найпростіший!

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

ROT ... 1? Дійсно? ОП навіть назвав ROT13 прикладом того, що він не хотів робити.
користувач812786
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.