เข้ารหัสและถอดรหัสสตริงใน C # หรือไม่


695

ฉันจะเข้ารหัสและถอดรหัสสตริงใน C # ได้อย่างไร


2
ตรวจสอบลิงค์นี้codeproject.com/KB/recipes/Encrypt_an_string.aspx
พัฒนา

3
ต้องการอะไรบางอย่างง่าย ... การเชื่อมโยงนี้ทำงานให้ฉันsaipanyam.net/2010/03/encrypt-query-strings.html
MRM

6
ฉันขอแนะนำให้ลด 3DES และใช้ AES-GCM ไม่พบ AES-GCM ใน. NET 4.5 crypto libs และแตกต่างจาก 'AES ปกติ' (ปกติ = โหมด AES-CBC) AES-GCM นั้นดีกว่า AES 'ปกติ' มากสำหรับเหตุผลการเข้ารหัสที่ฉันจะไม่เข้าไป ดังนั้นjbtuleมีคำตอบที่ดีที่สุดด้านล่างนี้ภายใต้Bouncy Castle AES-GCMหมวด ถ้าคุณไม่เชื่อเราที่ไว้วางใจน้อยผู้เชี่ยวชาญที่เอ็นเอสเอ (NSA Suite B @ nsa.gov/ia/programs/suiteb_cryptography/index.shtml : The Galois/Counter Mode (GCM) is the preferred AES mode.)
DeepSpace101

1
@Sid ส่วนตัวฉันชอบ AES-CBC + HMAC-SHA2 มากกว่า AES-GCM สำหรับสถานการณ์ส่วนใหญ่ GCM ล้มเหลวอย่างรุนแรงหากคุณนำ nonce มาใช้ซ้ำ
CodesInChaos

2
@ ไซด์ Nonce นำมาใช้ใหม่เป็นความคิดที่ไม่ดีใช่ แต่ฉันเห็นว่ามันเกิดขึ้นแม้กับโปรแกรมเมอร์ / ผู้เข้ารหัสที่เชี่ยวชาญ หากเกิดเหตุการณ์ดังกล่าว GCM จะพังทลายลงอย่างสิ้นเชิงในขณะที่ CBC + HMAC จะพัฒนาจุดอ่อนเล็กน้อยเท่านั้น ด้วย SSL เช่นโปรโตคอล GCM นั้นใช้ได้ แต่ฉันไม่พอใจกับมันเนื่องจาก API "เข้ารหัสและรับรองความถูกต้อง" มาตรฐาน
CodesInChaos

คำตอบ:


415

แก้ไข 2013 - ต.ค. : แม้ว่าฉันจะแก้ไขคำตอบนี้เมื่อเวลาผ่านไปเพื่อแก้ไขข้อบกพร่องโปรดดูคำตอบของ jbtuleสำหรับโซลูชั่นที่มีประสิทธิภาพและมีข้อมูลมากขึ้น

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

คำตอบเดิม:

นี่คือตัวอย่างการทำงานที่ได้มาจาก"RijndaelManaged Class" เอกสารและชุดฝึกอบรม MCTS

แก้ไขปี 2012 ถึงเดือนเมษายน : คำตอบนี้ถูกแก้ไขเพื่อจ่ายค่า IV ล่วงหน้าตามคำแนะนำของ jbtule และดังที่นี่:

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

โชคดี!

public class Crypto
{

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return plaintext;
    }

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

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

        return buffer;
    }
}

3
เพื่อ Bret - สวัสดีขอบคุณสำหรับตัวอย่างของคุณ อาจคิดว่า - ฉันมีปัญหากับ key leng - ฉันทำการดัดแปลงกับ MD5 ดังนั้นถ้าใครบางคนจะใช้ตัวอย่างของคุณในคุณลักษณะกรุณาใช้สิ่งนี้สำหรับการทำให้เป็นมาตรฐานของคีย์ (หรือคุณสามารถใช้ hgor algoritm อื่น ๆ : HashAlgorithm hash = MD5CryptoServiceProvider UnicodeEncoding UE = ใหม่ UnicodeEncoding (); byte [] key = hash.ComputeHash (UE.GetBytes (เข้ารหัส _password)); ps: ขออภัยในภาษาอังกฤษของฉัน :) slinti

18
รหัสข้างต้นไม่ปลอดภัยมันแบ่งกฎพื้นฐานของความปลอดภัยทาง semantic กับ aes คุณไม่ควรใช้ IV เดียวกันมากกว่าหนึ่งครั้งด้วยคีย์เดียวกัน สิ่งนี้จะให้ IV เหมือนกันทุกครั้งที่คุณใช้คีย์เดียวกัน
jbtule

7
การใช้เกลือในกระบวนการหากุญแจจะไม่เจ็บ ค่าคงที่ไม่ใช่เกลือที่ดีเช่นเดียวกับค่าคงที่ไม่ใช่ IV ที่ดี
CodesInChaos

5
เกี่ยวกับความสับสน AES กับ Rijndael: AES เป็นส่วนหนึ่งของ Rijndael หากคุณใช้ Rijndael กับบล็อก 128 บิตและคีย์ 128, 192 หรือ 256 บิตที่คุณใช้ AES
CodesInChaos

3
เกลือเพิ่มระดับของการทำให้งงงวยเพื่อป้องกันการแตกร้าว แนะนำให้คุณอ่านตัวอย่าง jbtules ด้านล่างที่ซึ่งเกลือถูกสร้างขึ้น
Brett

360

ตัวอย่างสมัยใหม่ของการเข้ารหัสสตริงที่ได้รับการพิสูจน์แล้วอย่างสมมาตร

แนวปฏิบัติทั่วไปที่ดีที่สุดสำหรับการเข้ารหัสแบบสมมาตรคือการใช้การรับรองความถูกต้องที่มีการเชื่อมโยงข้อมูล (AEAD) อย่างไรก็ตามนี่ไม่ใช่ส่วนหนึ่งของไลบรารี. net crypto มาตรฐาน ดังนั้นตัวอย่างแรกใช้AES256และHMAC256ซึ่งเป็นสองขั้นตอนเข้ารหัสแล้ว MACซึ่งต้องใช้ค่าใช้จ่ายเพิ่มเติมและปุ่มเพิ่มเติม

ตัวอย่างที่สองใช้วิธีปฏิบัติที่ง่ายกว่าของ AES256- GCMโดยใช้ปราสาท Bouncy open source (ผ่านทาง nuget)

ตัวอย่างทั้งสองมีฟังก์ชั่นหลักที่ใช้สตริงข้อความลับคีย์ (s) และเพย์โหลดที่ไม่เป็นความลับและเป็นทางเลือกและสตริงการเข้ารหัสที่ผ่านการรับรองความถูกต้องซึ่งมีตัวเลือกเสริมด้วยข้อมูลที่ไม่ใช่ความลับ เป็นการดีที่คุณจะใช้เหล่านี้มีคีย์ 256bit (s) NewKey()ที่สร้างแบบสุ่มดู

ตัวอย่างทั้งสองยังมีวิธีการช่วยเหลือที่ใช้รหัสผ่านสตริงเพื่อสร้างคีย์ วิธีการช่วยเหลือเหล่านี้มีไว้เพื่อความสะดวกของตรงกับตัวอย่างอื่น ๆ แต่พวกเขาจะห่างไกลความปลอดภัยน้อยลงเพราะความแข็งแรงของรหัสผ่านที่เป็นไปได้ไกลอ่อนแอกว่าคีย์ 256 บิต

อัปเดต: เพิ่มการbyte[]โอเวอร์โหลดและมีเพียงส่วนสำคัญเท่านั้นที่มีการจัดรูปแบบเต็มโดยมีการเว้นวรรค 4 รายการและ api docs เนื่องจากข้อ จำกัด ของคำตอบ StackOverflow


. NET Built-in Encrypt (AES) - จากนั้น MAC (HMAC) [สรุป]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      byte[] cipherText;
      byte[] iv;

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

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

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

          cipherText = cipherStream.ToArray();
        }

      }

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

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

    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      byte[] cryptKey;
      byte[] authKey;

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

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

Bouncy Castle AES-GCM [สรุป]

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

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

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

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

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


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

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

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


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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return plainText;
      }

    }

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

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

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

      var generator = new Pkcs5S2ParametersGenerator();

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

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

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

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

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

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

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

      var generator = new Pkcs5S2ParametersGenerator();

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

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

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

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

7
มีตัวอย่างเหล่านี้โพสต์ในการตรวจสอบรหัสด้วย
jbtule

3
นั่นเป็นคำถามที่ดีเหล่านี้จะใช้รับรองความถูกต้องเข้ารหัสตัวอย่างที่นอกเหนือไปจากการเข้ารหัสพวกเขามีMAC เพื่อตรวจสอบว่า ciphertext ไม่ได้รับการ modifed โดยคนอื่นนี้เป็นหลักเพื่อป้องกันการโจมตีได้รับการแต่งตั้ง-ciphertext ดังนั้นเมื่อถอดรหัสจะคำนวณ MAC เพื่อตรวจสอบกับส่วนที่ผนวกเข้าเพื่อตรวจสอบความถูกต้องหากตรวจสอบความถูกต้องแล้วจะถอดรหัสและถ้ามันไม่ส่งคืน null
jbtule

3
การตรวจสอบอาเรย์บน MAC ทำทุกดัชนีเพราะการโจมตีเวลาสามารถใช้ในการคำนวณ MAC ใหม่ในไซเฟอร์เท็กซ์ปลอมหากมันส่งกลับไบต์แรกที่ไม่ตรงกัน
jbtule

5
นั่นเป็นหนังสือที่ดีและค่อนข้างใหม่ สิ่งที่ฉันอยากจะแนะนำเพิ่มเติมคือCryptography Iออนไลน์โดย Dan Boneh วิดีโอที่ดีจริงๆการทดสอบที่ดีและปัญหาของเครื่องที่ดีเช่นกันซึ่งเป็นพื้นฐานที่ดีสำหรับการใช้งานการเข้ารหัส คุณควรใช้สิ่งที่คุณพอใจที่สุดเกี่ยวกับ AesCryptoServiceProvider
jbtule

8
ส่วนการใช้งานที่อธิบายอย่างดีจะมีประโยชน์อย่างยิ่ง
Rocklan

108

นี่คือตัวอย่างการใช้ RSA

สิ่งสำคัญ:จะไม่มีการ จำกัด ขนาดของข้อมูลที่คุณสามารถเข้ารหัสด้วยการเข้ารหัส RSA KeySize - MinimumPaddingคือ เช่น 256 ไบต์ (สมมติว่าเป็นคีย์ 2048 บิต) - 42 ไบต์ (การเสริมเต็ม OEAP ขั้นต่ำ) = 214 ไบต์ (ขนาดธรรมดาสูงสุด)

แทนที่ your_rsa_key ด้วยคีย์ RSA ของคุณ

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

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

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

สำหรับข้อมูลเพิ่มเติมโปรดเยี่ยมชมMSDN - RSACryptoServiceProvider


7
ขออภัยที่จะถามคำถามง่าย ๆ แต่ทุกคนสามารถบอกฉันได้ที่ไหนว่าฉันจะได้รับหมายเลข RSA หรือฉันจะสร้างได้อย่างไร
Akash Kava

11
ทำไมต้อง RSA RSA มีการใช้งาน แต่ไม่มีอะไรระบุว่านี่เป็นหนึ่งในนั้น
CodesInChaos

39
แม้ในคำถามเดิมไม่มีข้อบ่งชี้ว่าRSAอาจจะเหมาะสม การเข้ารหัสแบบอสมมาตรมีประโยชน์ แต่ก็ไม่ใช่ทางเลือกที่เหมาะสมสำหรับการเข้ารหัสเริ่มต้น โค้ดตัวอย่างของคุณจะล้มเหลวสำหรับสตริงที่ยาวกว่าเนื่องจากคลาส RSA ไม่ได้ออกแบบมาเพื่อการเข้ารหัสทั่วไป หากคุณต้องการคุณสมบัติไม่สมมาตรคุณควรเข้ารหัสคีย์สมมาตรด้วย RSA และเข้ารหัสข้อมูลจริงด้วยคีย์สมมาตรนั้น ดังนั้นฉันยังเชื่อว่าคำตอบของคุณคือคำแนะนำที่ไม่ดี
CodesInChaos

9
ฉันประทับใจ 70 คะแนนสำหรับคำตอบที่ผิด !!! ขณะที่ CodesInChaos กล่าวว่าสำหรับการเข้ารหัสประเภทนี้คุณต้องใช้คีย์ Symmetric ไม่ใช่ Assymetric
Otto Kanellis

5
มันไม่ได้เป็นคำตอบที่ผิดเพียงแค่ยุ่งกับค่าใช้จ่ายจำนวนมาก ... ใช้ AES / วิธี Symmetric อื่น ๆ เพื่อผลลัพธ์ที่ดีกว่า
Tomer W

54

หากคุณใช้ ASP.Net คุณสามารถใช้ฟังก์ชันที่มีอยู่แล้วภายใน. Net 4.0 เป็นต้นไป

System.Web.Security.MachineKey

สุทธิ 4.5 มีและMachineKey.Protect()MachineKey.Unprotect()

สุทธิ 4.0 มีและMachineKey.Encode() MachineKey.Decode()คุณควรตั้งค่า MachineKeyProtection เป็น 'ทั้งหมด'

นอก ASP.Net คลาสนี้ดูเหมือนจะสร้างคีย์ใหม่ทุกครั้งที่รีสตาร์ทแอปจึงไม่ทำงาน ด้วยการมองอย่างรวดเร็วใน ILSpy ดูเหมือนว่าฉันจะสร้างค่าเริ่มต้นของตัวเองหากการตั้งค่าแอพที่เหมาะสมหายไป ดังนั้นคุณอาจตั้งค่าภายนอก ASP.Net ได้

ฉันไม่สามารถค้นหา non-ASP.Net ที่เทียบเท่าได้นอกเนมสเปซ System.Web


ใครสามารถบอกฉันได้ว่าทำไมคำตอบนี้มีคะแนนน้อยมาก ดูเหมือนว่าจะเป็นวิธีที่สะดวกมากสำหรับแอปพลิเคชัน ASP.NET
Dirk Boer

@DirkBoer ฟังก์ชั่นถูกเพิ่มสองสามปีหลังจากที่คำถามถูกถามฉันได้เพิ่มคำตอบของคำถามนี้เพื่อให้คนรู้ว่ามีวิธีที่ง่ายขึ้นในวันนี้ นอกจากนี้ยังใช้งานได้กับ ASP.Net โดยไม่ต้องมี app.config-fu ซึ่งเป็นอันตรายหากคุณไม่รู้ว่ากำลังทำอะไรอยู่
mattmanser

3
ให้อภัยความไม่รู้ของฉัน แต่จากหน้าเว็บฉันไม่สามารถหาคำตอบได้ หากฉันเข้ารหัสสตริงในเครื่องหนึ่งให้เขียนไปยังฐานข้อมูลและอ่านด้วยเครื่องอื่นฉันจะสามารถถอดรหัสได้ตราบใดที่พารามิเตอร์วัตถุประสงค์มีค่าเท่ากันหรือไม่ บางทีฉันอาจสับสนด้วยชื่อคลาส "MachineKey"
Adriaan Davel

คำถามอื่นฉันสามารถใช้สิ่งนี้ในแอพ WPF ได้หรือไม่ มันไม่มีการอ้างอิงทางเว็บมันเป็นการดีที่จะเพิ่มการอ้างอิงไปยัง System.Web?
Adriaan Davel

2
@AdriaanDavel ต่อเอกสารที่เชื่อมโยง "ควรใช้ MachineKey APIs ในแอพ ASP.NET เท่านั้นพฤติกรรมของ MachineKey APIs นอกบริบทของแอปพลิเคชัน ASP.NET นั้นไม่ได้กำหนด" - ใช้เฉพาะเมื่อคุณสนุกกับเกมของ Russian Roulette
Mark Sowul

47

BouncyCastleเป็นห้องสมุด Crypto ที่ยอดเยี่ยมสำหรับ. NET มันมีให้ในรูปแบบของNugetแพ็คเกจสำหรับติดตั้งในโครงการของคุณ ฉันชอบมากกว่าสิ่งที่มีอยู่ในไลบรารี System.Security.Cryptography ในปัจจุบัน มันทำให้คุณมีตัวเลือกมากขึ้นในแง่ของอัลกอริธึมที่มีอยู่และให้โหมดเพิ่มเติมสำหรับอัลกอริธึมเหล่านั้น

นี่เป็นตัวอย่างของการนำTwoFishมาใช้ซึ่งเขียนโดยBruce Schneier (ฮีโร่สำหรับพวกเราทุกคนหวาดระแวงที่นั่น) มันเป็นอัลกอริทึมแบบสมมาตรเช่น Rijndael (aka AES) มันเป็นหนึ่งในสามของผู้เข้ารอบสุดท้ายสำหรับมาตรฐาน AES และพี่น้องในอัลกอริทึมที่มีชื่อเสียงอีกอันที่เขียนโดย Bruce Schneier ชื่อ BlowFish

สิ่งแรกที่มี bouncycastle คือการสร้างคลาสของตัวเข้ารหัสซึ่งจะทำให้ง่ายต่อการติดตั้งรหัสบล็อกอื่น ๆ ภายในห้องสมุด คลาสของตัวเข้ารหัสต่อไปนี้ใช้ในอาร์กิวเมนต์ทั่วไป T โดยที่ T ใช้ IBlockCipher และมีตัวสร้างเริ่มต้น

การปรับปรุง: เนื่องจากความต้องการที่เป็นที่นิยมฉันได้ตัดสินใจที่จะใช้การสร้าง IV แบบสุ่มเช่นเดียวกับรวม HMAC ในชั้นนี้ แม้ว่าจากมุมมองของสไตล์สิ่งนี้ขัดกับหลักการ SOLID ของความรับผิดชอบเดี่ยวเนื่องจากลักษณะของสิ่งที่ชั้นนี้ฉันไม่ได้เปลี่ยน คลาสนี้จะใช้พารามิเตอร์ทั่วไปสองตัวหนึ่งตัวสำหรับรหัสและอีกอันสำหรับย่อย มันสร้าง IV โดยอัตโนมัติโดยใช้ RNGCryptoServiceProvider เพื่อจัดทำ RNG ที่ดีและช่วยให้คุณใช้อัลกอริธึมย่อยที่คุณต้องการจาก BouncyCastle เพื่อสร้าง MAC

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

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

    private IBlockCipher blockCipher;

    private BufferedBlockCipher cipher;

    private HMac mac;

    private byte[] key;

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

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

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

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

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

        var iv = this.GenerateIV();

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

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

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

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

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

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

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

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

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

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

        return AreEqual(digest,computed);
    }

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

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

        return result == 0;
    }

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

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

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

            return result;
        }
    }

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

        return result;
    }
}

ถัดไปเพียงแค่เรียกใช้วิธีการเข้ารหัสและถอดรหัสในคลาสใหม่นี่คือตัวอย่างการใช้ twofish:

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

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

เป็นเรื่องง่ายที่จะเปลี่ยนรหัสบล็อกอื่นเช่น TripleDES:

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

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

ในที่สุดหากคุณต้องการใช้ AES กับ SHA256 HMAC คุณสามารถทำสิ่งต่อไปนี้:

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

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

ส่วนที่ยากที่สุดเกี่ยวกับการเข้ารหัสนั้นเกี่ยวข้องกับกุญแจไม่ใช่อัลกอริทึม คุณจะต้องคิดถึงว่าคุณเก็บกุญแจไว้ที่ไหนและถ้าคุณต้องคุณจะแลกเปลี่ยนกุญแจอย่างไร อัลกอรึทึมเหล่านี้มีความสามารถในการทดสอบเวลาได้ดีและยากที่จะทำลายได้ คนที่ต้องการขโมยข้อมูลจากคุณจะไม่ใช้เวลาชั่วนิรันดร์ในการเข้ารหัสข้อความของคุณพวกเขาจะพยายามหาว่ากุญแจของคุณอยู่ที่ไหนหรือที่ไหน ดังนั้น # 1 เลือกคีย์ของคุณอย่างชาญฉลาด # 2 เก็บไว้ในที่ปลอดภัยถ้าคุณใช้ web.config และ IIS คุณสามารถเข้ารหัสบางส่วนของ web.configและในที่สุดถ้าคุณต้องแลกเปลี่ยนกุญแจต้องแน่ใจว่า โปรโตคอลสำหรับการแลกเปลี่ยนกุญแจปลอดภัย

อัพเดต 2 เปลี่ยนวิธีเปรียบเทียบเพื่อลดการโจมตีตามจังหวะเวลา ดูข้อมูลเพิ่มเติมได้ที่นี่http://codahale.com/a-lesson-in-timing-attacks/ อัปเดตเป็นค่าเริ่มต้นเป็น padding PKCS7 และเพิ่มนวกรรมิกใหม่เพื่อให้ผู้ใช้สามารถเลือกได้ว่าต้องการใช้ช่องว่างใด ขอบคุณ @CodeInChaos สำหรับคำแนะนำ


3
1) คลาสนั้นค่อนข้างน่ารำคาญที่จะใช้เนื่องจากคุณใส่ภาระการจัดการ IV ให้กับผู้ใช้ที่จะทำให้มันผิดอย่างแน่นอน 2) การขาด MAC ทำให้เกิดช่องโหว่ที่ทำให้เกิดช่องว่างภายใน
CodesInChaos

3
1) ช่องว่างภายในของคุณดูแตกสำหรับฉัน คุณเพิ่มช่องว่างภายในเป็นศูนย์และอย่าลบออก การเติมเต็มศูนย์เป็นความคิดที่ไม่ดีเนื่องจากไม่สามารถลบได้อย่างน่าเชื่อถือ ใช้ padding PKCS # 7 แทน ฉันคาดหวังว่าฟังก์ชั่นการเข้ารหัส / ถอดรหัส bouncycastle จะสนับสนุนสิ่งนี้อยู่แล้ว 2) คุณควรใช้การเปรียบเทียบเวลาอย่างต่อเนื่องเพื่อตรวจสอบ MAC SequenceEqualไม่ วิธีนี้จะหลีกเลี่ยงช่องสัญญาณด้านเวลาที่รั่วไหลว่าคำนำหน้าของ MAC ที่นำเสนอและการจับคู่ MAC ที่แท้จริงนั้นนานแค่ไหน
CodesInChaos

2
@CodesInChaos ฉันเห็นด้วยขอบคุณสำหรับการตรวจสอบนี้ฉันได้ทำการแก้ไขเพื่อแก้ไขปัญหาคู่เหล่านี้ - nerdybeardo
nerdybeardo

คำตอบที่ดีเพียงคำถามเดียว .... สิ่งที่จะเป็นกุญแจสำคัญและ hmacKey ฉันใหม่เพื่อ crypto .. ขอบคุณ!
Terkhos

1
@Therkhos คุณควรใช้ตัวสร้างตัวเลขสุ่มที่ปลอดภัยสำหรับการสร้างคีย์เช่น RNGCryptoServiceProvider คุณไม่ควรใช้ข้อความรหัสผ่านหรือสิ่งที่คาดเดาได้ คุณควรใช้ความยาวสูงสุดที่อัลกอริทึมจะให้ตัวอย่างเช่น AES 256 ใช้ขนาดคีย์ที่ยาว 256 บิตดังนั้น 32 สุ่มไบต์จะดีที่สุดโดยปกติขนาดคีย์ HMAC จะขึ้นอยู่กับขนาดของอัลกอริทึมเช่น SHA2 ( 256) คีย์ 256 บิตที่สร้างโดยเครื่องมือสร้างตัวเลขสุ่มที่ปลอดภัยจะพอเพียง เปลี่ยนคีย์บ่อย! ยิ่งบ่อยยิ่งดี!
nerdybeardo

18

ข้อสงวนสิทธิ์: วิธีการแก้ปัญหานี้ควรใช้กับข้อมูลที่เหลือซึ่งไม่เปิดเผยต่อสาธารณะ (ตัวอย่างเช่น - ไฟล์กำหนดค่าหรือฐานข้อมูล) เฉพาะในสถานการณ์นี้โซลูชันรวดเร็วและสกปรกสามารถพิจารณาได้ดีกว่าโซลูชันของ @ jbtule เนื่องจากการบำรุงรักษาต่ำ

โพสต์ต้นฉบับ: ฉันพบว่าคำตอบของjbtuleมีความซับซ้อนเล็กน้อยสำหรับการเข้ารหัสสตริง AES ที่รวดเร็วและสกปรกและคำตอบของเบร็ทมีข้อผิดพลาดกับ Initialization Vector เป็นค่าคงที่ทำให้เสี่ยงต่อการถูกโจมตีจากช่องว่างภายใน เพิ่ม IV แบบสุ่มที่เพิ่มเข้าไปในสายการผลิตชิปโดยสร้างมูลค่าการเข้ารหัสที่แตกต่างกันการเข้ารหัสแต่ละครั้งมีค่าเท่ากัน:

การเข้ารหัสลับ:

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

ถอดรหัส:

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

แทนที่ EncryptionKey ด้วยรหัสของคุณ ในการติดตั้งของฉันคีย์จะถูกบันทึกไว้ในไฟล์การกำหนดค่า (web.config \ app.config) เนื่องจากคุณไม่ควรบันทึกรหัสฮาร์ดโค้ด ไฟล์การกำหนดค่าควรได้รับการเข้ารหัสเช่นกันดังนั้นคีย์จะไม่ถูกบันทึกเป็นข้อความที่ชัดเจน

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

        return _Key;
    }
}

1
ในขณะที่Encryptวิธีการของคุณสร้างค่าที่แตกต่างกันสำหรับการโทรทุกครั้งแม้จะมีข้อความธรรมดาเหมือนกัน แต่Substring(20)จะเหมือนกันทุกครั้งใช่ไหม
สไตล์พากย์

คุณหมายถึงอะไร "เหมือนกัน"? ฟังก์ชั่นถอดรหัสใช้ค่าทุกค่าที่สร้างขึ้นโดยฟังก์ชั่นการเข้ารหัสแยกมันออกจากเวกเตอร์เริ่มต้นและค่าที่เข้ารหัสและถอดรหัส
Gil Cohen

1
ฉันไม่ได้สังเกตว่าการEncryptสร้าง IV ที่แตกต่างกันในแต่ละครั้ง ด้วยเหตุผลบางอย่างฉันคิดว่า IV เหมือนเดิมทุกครั้งซึ่งโดยทั่วไปจะทำให้มันไม่มีจุดหมาย
dub stylee

1
@GilCohen เอาข้อจำกัดความรับผิดชอบนี้มาบอกว่าจะใช้เฉพาะข้อมูลที่เหลืออย่าเปิดเผยด้วยบริการจากนั้นคุณสามารถเรียกร้องการจัดการความเสี่ยงได้ อย่างไรก็ตามความรวดเร็วและสกปรกของคุณเป็นเพียงเลอะเทอะ ตัวอย่างเช่นทำไมคุณแทนที่ช่องว่างด้วยเครื่องหมายบวกที่เพิ่งถอดรหัสและไม่กลับกันเป็นเพราะสิ่งอื่นกำลังแก้ไข ciphertext ก่อนที่คุณจะได้รับ? เช่นเดียวกับการส่งผ่านสตริงข้อความค้นหา url คุกกี้หรือตัวแปรฟอร์ม hmm ซึ่งฟังดูเหมือนบริการซึ่งเป็นสิ่งที่จำเป็นเมื่อคุณต้องตรวจสอบสิทธิ์ข้อความตัวเลข
jbtule

2
@jbtule ไม่จริงนี่คือการเข้ารหัสของฟังก์ชัน Base64 ด้วยเหตุผลบางอย่าง นี่เป็นข้อมูลที่ใช้จริงและฉันเห็นด้วยกับความคิดเห็นของคุณ ฉันจะเพิ่ม
Gil Cohen

12

การเข้ารหัสลับ

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

ถอดรหัส:

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

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

8
-1 สิ่งนี้อ่อนแอมาก 1) DES เป็นเรื่องง่ายที่จะดุร้ายกำลังมีคีย์ 56 บิต 2) กุญแจคือเลขฐานสองไม่ใช่ UTF8 หากคีย์ประกอบด้วยอักขระ ASCII (น่าจะเป็นทางปฏิบัติ) สิ่งนี้จะลดขนาดของคีย์ที่มีประสิทธิภาพเป็น 48 บิต 3) IV ควรแตกต่างกันไปสำหรับแต่ละข้อความ 4) การขาด MAC ทำให้คุณเปิดการโจมตีที่แอคทีฟ
CodesInChaos

9
+1 OP มีคำถามง่าย ๆ โดยไม่จำเป็นต้องใช้ความแข็งแกร่งสูงสุดและคำตอบนี้สอดคล้องกับคำตอบนั้นอย่างสมบูรณ์แบบ อย่างน้อยฉันก็สามารถใช้สิ่งนี้ได้เพราะฉันมีการใช้งานที่ง่ายสำหรับการเข้ารหัส
Roland

-1 @Roland ตามที่ระบุไว้โดย CodesInChaos IV จำเป็นต้องแตกต่างกันไปในแต่ละข้อความหากไม่เป็นเช่นนั้นคุณกำลังใช้ API อย่างไม่ถูกต้องดังนั้นจึงไม่ควรใช้รหัสนี้ ระยะเวลา จะไม่บดบังคีย์ 48 บิตทำให้สามารถถอดรหัสนี้ได้กับทุกคนที่ไม่มีคีย์ในเวลาเพียงหนึ่งวันดังนั้นนี่จึงไม่มีการเข้ารหัสอีกต่อไปดังนั้นจึงไม่ตอบคำถาม
jbtule

@jbtule ขอบคุณฉันจะพิจารณาคำตอบอื่น ๆ อย่างจริงจังเมื่อตรวจสอบโครงการเข้ารหัสของฉัน
Roland

2
ใช้สิ่งนี้สำหรับแอปพลิเคชันแบบง่ายหากคุณกำลังปกป้องความลับทางนิวเคลียร์ งานนี้เป็น
John Pittaway

6

ด้วยการอ้างอิงของการเข้ารหัสและถอดรหัสสตริงใน c #ฉันพบหนึ่งในโซลูชันที่ดี:

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

สำหรับการเข้ารหัส

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

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

    byte[] cipherTextBytes;

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

สำหรับถอดรหัส

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

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

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

7
เกลือ Hardcoded และ IV และการใช้การแทน ASCII สำหรับพวกเขาสิ่งนี้ผิดทุกชนิด
jbtule

7
คำเตือนความปลอดภัย: อย่าใช้รหัสนี้ดูความคิดเห็นของฉันด้านบน
jbtule

6
ฉันขอโทษที่ฉันไม่ได้สะกดคำนี้ IV ไม่ใช่กุญแจสำคัญและการรักษาความลับให้ความปลอดภัยเพิ่มเป็นศูนย์และทำให้การคาดการณ์สูญเสียความปลอดภัยได้เล็กน้อย Hardcoding the IV นั้นไม่มีเหตุผล / ไร้เหตุผล / ผิดกับคนที่รู้วิธีใช้การเข้ารหัส AES-CBC อย่างสมบูรณ์ Encoding.ASCII.GetBytesสำหรับข้อมูลที่มีวัตถุประสงค์เพื่อเพิ่มเอนโทรปีกับสิ่งที่มนุษย์ได้รับการแต่งตั้งเป็นไปได้น้อยแล้ววิธีเอนโทรปีกว่าที่คาดไว้และเป็นมากผิดพลาดสามเณร สิ่งเหล่านี้เป็นสิ่งที่แก้ไขได้อย่างง่ายดาย แต่คุณไม่ได้ดังนั้นคำเตือนที่ชัดเจนของฉันยังคงอยู่เนื่องจากผลกระทบด้านความปลอดภัย
jbtule

4
ราหุลชิลล์ลง! เอนหลังผ่อนคลายและคิดว่าทำไมทั้ง 3 ความคิดเห็นจาก @jbtule จึงได้รับการโหวตมากขึ้น เขากำลังพูดถึงสิ่งที่มีเหตุผลเพื่อที่จะนำคุณไปสู่เส้นทางที่ถูกต้อง ไม่มีอะไรจะรู้สึกขุ่นเคือง คุณยังใหม่กับ SO คุณจะรู้ว่ามันทำงานอย่างไรในที่สุด
Nikhil Vartak

5

ตัวอย่างต่อไปนี้สาธิตวิธีการเข้ารหัสและถอดรหัสข้อมูลตัวอย่าง:

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

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

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

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

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

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

ขอบคุณ @reza .. ฉันจะใช้มันกับโปรเจ็กต์บ้านถ้าฉันจะทำ
Arrie

4

เพื่อสนับสนุนคำตอบ mattmanser นี่คือตัวอย่างการใช้คลาส MachineKey เพื่อเข้ารหัส / ถอดรหัสค่าที่ปลอดภัยของ URL

สิ่งที่ต้องคำนึงถึงตามที่กล่าวไว้ก่อนหน้านี้จะใช้การตั้งค่าเครื่อง ( https://msdn.microsoft.com/en-us/library/ff649308.aspx ) คุณสามารถตั้งค่าการเข้ารหัสและถอดรหัสคีย์ / อัลกอริทึมด้วยตนเอง (คุณอาจต้องใช้สิ่งนี้เป็นพิเศษหากไซต์ของคุณกำลังทำงานบนเซิร์ฟเวอร์หลายเครื่อง) ในไฟล์ web.config คุณสามารถสร้างกุญแจจาก IIS (ดูที่นี่: https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-) the-iis-manager / ) หรือสามารถใช้ตัวสร้างคีย์เครื่องออนไลน์เช่น: http://www.developerfusion.com/tools/generatemachinekey/

    private static readonly UTF8Encoding Encoder = new UTF8Encoding();

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

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

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

        return string.Empty;
    }

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

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

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

        return string.Empty;
    }

3

นี่คือตัวอย่างง่ายๆของการเข้ารหัสสตริงใน C # โดยใช้โหมด AES CBC พร้อมด้วยปุ่ม IV และ HMAC และรหัสผ่านที่ได้จากการสุ่มเพื่อแสดงชิ้นส่วนที่เคลื่อนไหวพื้นฐาน:

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

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

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

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

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

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

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

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

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

    return mismatch != 0;
}

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

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

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

        var computedMac = hmac.ComputeHash(packed);

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

        return packed;
    }            
}

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

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

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

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

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

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

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

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

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

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

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

    return Encoding.UTF8.GetString(plaintext);
}

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

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

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

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

    Console.WriteLine(recovered);
}

3
ปัญหาสองสามข้อ: 1) คุณไม่ได้ใช้เกลือในการได้มาซึ่งคีย์ซึ่งทำให้สามารถโจมตีได้หลายเป้าหมาย 2) ฟังก์ชั่นการเปรียบเทียบ MAC ของคุณอาจเสี่ยงต่อการโจมตีจากช่องทาง / จังหวะเวลาเนื่องจากคุณแยกข้อมูลลับ ใช้สิ่งที่ชอบmismatch += found[i]^computed[i]แทน 3) คุณใช้ PBKDF2-HMAC-SHA-1 มากกว่า 20 ไบต์ซึ่งทำให้ KDF ของคุณช้าลงด้วยปัจจัยที่ 2 โดยไม่ทำให้การโจมตีช้าลง
CodesInChaos

1
@CodesInChaos: 1) นี่เป็นตัวอย่างง่ายๆที่จะช่วยให้ผู้คนเริ่มต้น - ฉันละเว้นการสุ่มเกลือเพื่อความชัดเจนเท่านั้น แต่จุดที่ดี 2) ดีจุดที่ลึกซึ้ง 3) คุณแนะนำอะไรให้รับคีย์ 16 ไบต์สองตัวในยี่สิบไบต์
Jim Flood

วิธีที่ง่ายที่สุดคือการแฮชเอาต์พุตของแฮชช้าด้วย SHA-2 วิธีที่น่าสนใจกว่าคือ HKDF หรือเพียงแค่ใช้ PBKDF2 อีกครั้ง แต่คราวนี้ตั้งค่าการทำซ้ำเป็น 1
CodesInChaos

@CodesInChaos ฉันจะไม่ใช้ SHA-2 งานของฟังก์ชันแฮชไม่เหมือนกับงานของฟังก์ชันการสืบทอดคีย์ แฮชจะต้องคาดเดาไม่ได้และจะเปลี่ยนเมื่ออินพุตเปลี่ยนเท่านั้น กุญแจจะต้องแยกไม่ออกจากการสุ่ม ฉันจะยังวาด 32 ไบต์จาก KDF ในกรณีนี้คุณเพิ่มประสิทธิภาพเร็วเกินไปและเพิ่มความเสี่ยง
Jim Flood

3

ทางเลือกในการ BouncyCastle สำหรับAES-GCMเข้ารหัสlibsodium สุทธิ มันล้อมไลบรารี libsodium C ข้อดีอย่างหนึ่งก็คือมันใช้ส่วนขยาย AES-NI ในซีพียูเพื่อการเข้ารหัสที่รวดเร็วมาก ข้อเสียคือมันจะไม่ทำงานเลยหาก CPU ไม่มีส่วนขยาย ไม่มีซอฟต์แวร์ถอยกลับ


3

รหัสต่อไปนี้เป็นรุ่นปรับปรุงของ Ghazal ของคำตอบจะคล้ายคำถาม

public class EncryptionHelper
{
    private Aes aesEncryptor;

    public EncryptionHelper()
    {
    }

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

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

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

2

นี่คือคลาสที่ถูกวางไว้ที่นี่โดยเบร็ท อย่างไรก็ตามฉันทำการแก้ไขเล็กน้อยเนื่องจากฉันได้รับข้อผิดพลาด 'ความยาวไม่ถูกต้องสำหรับอาเรย์ของ Base-64' เมื่อใช้เพื่อให้สตริง URL มีการเข้ารหัสและถอดรหัส

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

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

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

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

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

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

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

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

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

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

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

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

        byte[] inputByteArray;

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

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

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

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

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

        return plaintext;
    }

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

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

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

        return buffer;
    }

}

1
คืออะไรConvertStringArrayToString()วิธีใช้?
abenci

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

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

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

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

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

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

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

                    return msEncrypt.ToArray();
                }
            }
        }

    }

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

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

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

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

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


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

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

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

    #endregion
}

MD5 นั้นปลอดภัยน้อยที่สุด ไม่แนะนำ
vapcguy

1

การเข้ารหัสเป็นเรื่องธรรมดามากในการเขียนโปรแกรม ฉันคิดว่าเป็นการดีกว่าที่จะติดตั้งแพ็คเกจเพื่อทำงานให้คุณ อาจเป็นโปรเจค Nuget แบบโอเพ่นซอร์สอย่างง่ายเช่น Simple Aes Encryption

กุญแจอยู่ในไฟล์กำหนดค่าและทำให้ง่ายต่อการเปลี่ยนแปลงในสภาพแวดล้อมการผลิตและฉันไม่เห็นข้อเสียใด ๆ

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

1
ข้อเสียเปรียบที่ยิ่งใหญ่คือมันไม่ใช่การเข้ารหัสที่รับรองความถูกต้อง
jbtule

0

คัดลอกในของฉันคำตอบที่นี่จากคำถามที่คล้ายกัน: การเข้ารหัสแบบสองทางที่ง่ายสำหรับ C #

อิงจากคำตอบและความคิดเห็นที่หลากหลาย

  • เวกเตอร์เริ่มต้นแบบสุ่มนำเสนอต่อข้อความ crypto (@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 /programming/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

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

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

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

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

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

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

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

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

1
คุณควรเพิ่ม MAC เพื่อป้องกันการโจมตีที่แอ็คทีฟเช่น padding oracles
CodesInChaos

คุณอาจจะถูกต้องฉันไม่มีความเชี่ยวชาญในพื้นที่นี้ เมื่อฉันไปที่หัวข้อนี้ในตอนแรกฉันแค่ต้องการบางสิ่งที่ง่ายและปลอดภัย ฉันจะใช้ห้องสมุดที่ทดลองแล้วและเป็นจริงสำหรับข้อมูลที่ละเอียดอ่อนมาก
angularsen

0

นี่เป็นตัวอย่างง่ายๆโดย ASP Snippets

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


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

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

1
คุณไม่ตรวจสอบความถูกต้อง / การตรวจสอบความถูกต้อง คุณควรเพิ่ม MAC
Artjom B.

สิ่งที่คุณหมายถึงจริงตัวอย่างข้างต้นคือการเข้ารหัส / ถอดรหัสตัวแปรสตริง
Vijay Kumbhoje

3
ไซเฟอร์เท็กซ์ควรได้รับการรับรองความถูกต้อง (เช่นกับ HMAC) เพื่อป้องกันการโจมตีของช่องว่างภายใน เมื่อฉันดูรหัสนี้อีกครั้งดูเหมือนว่าคุณกำลังใช้โหมด ECB ซึ่งไม่ควรใช้เพราะไม่ปลอดภัยทางความหมาย นอกจากนี้เมื่อคุณได้รับคีย์และ IV จากมาสเตอร์คีย์และเกลือเกลือจะคงที่ สิ่งนี้นำไปสู่ ​​IV แบบคงที่ซึ่งแบ่งแนวคิดทั้งหมดของ IV และทำให้โครงร่างของคุณไม่ปลอดภัยทางความหมายอีกครั้ง
Artjom B.

ขอบคุณ Brother มันจะช่วยได้มากถ้าคุณสามารถส่งรหัสที่แก้ไขได้ที่นี่
Vijay Kumbhoje

0

อัลกอริทึม AES:

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

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

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

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

            string plaintext = null;

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

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

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

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

1) IV ถูกส่งผ่านเป็นพารามิเตอร์ซึ่งหมายความว่าผู้พัฒนาต้องทำการจัดการ IV และพวกเขาจะเข้าใจผิด แต่ควรสร้างและจัดเก็บ IV แบบสุ่มควบคู่ไปกับไซเฟอร์เท็กซ์ 2) เนื่องจาก IV และคีย์จะเปลี่ยนแปลงระหว่างการประมวลผลหลายEncryptionวิธีและไม่คงอยู่จึงไม่จำเป็นต้องมีวิธีนี้เลยนอกจากเพื่อการสาธิต 3) ไม่มีการพิสูจน์ตัวตนของไซเฟอร์เท็กซ์ดังนั้นผู้โจมตีอาจจัดการมันโดยที่คุณไม่ได้ตรวจจับมัน (ดู: padding oracle attack)
Artjom B.

hai @ArtjomB นักพัฒนาไม่จำเป็นต้องกังวลเกี่ยวกับการจัดการ iv เพราะมันจะถูกสร้างและเพิ่มพร้อมกับสตริงการเข้ารหัส
Skull

ฉันไม่เห็นด้วย ไอวีจะถูกเก็บไว้ใน_ivตัวแปรชั้นเรียนและไม่ได้เขียนลงในไซเฟอร์เท็กซ์ ดังนั้นคุณคิดว่าผู้รับจะรู้คีย์และ IV ได้อย่างไร? พวกเขาจะต้องได้รับการแจกจ่ายด้วยวิธีอื่น เนื่องจาก IV ไม่ควรเป็นความลับจึงควรสร้างแบบสุ่มสำหรับการเข้ารหัสแต่ละครั้งและกระจายไปพร้อมกับ ciphertext
Artjom B.


1
1) ในลิงก์ด้านบนคุณสามารถหาวิธีการนำเออีเอสไปใช้ได้โดยไม่จำเป็นต้องกังวลเกี่ยวกับการจัดการ iv เพราะ iv ยังได้รับการเข้ารหัสพร้อมกับสตริง 2) เนื่องจากฟังก์ชั่นที่คุณอ้างถึงมีโมดิฟายเออร์ส่วนตัวคุณไม่สามารถเรียกมันได้นอก Inorder การเข้ารหัสเราสามารถใช้ฟังก์ชัน Cryptographyclass เท่านั้นการเข้ารหัส ("SAMPLEstring")
Skull

0

นี่คือตัวอย่างวิธีการเข้ารหัส / ถอดรหัส AES-GCM โดยใช้แพคเกจ Bouncy castle

ฉันได้พบตัวอย่างนั้นเมื่อ googled เป็นไปได้ที่จะถอดรหัสข้อมูลจาก GOlang crypto/aesapi:

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

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

ตัวอย่าง. Net ทำงานเหมือนเครื่องรางที่มีคีย์ (256 บิต), mac (128 บิต) และ nonce (96 บิต)


-1

ตัวอย่างที่ดีวิธีใช้PGPCoreกับ BouncyCastle โซลูชันที่ง่ายมาก: https://blog.bitscry.com/2018/07/05/pgp-encrypt-and-decryption-in-c/

ฉันลองใช้วิธีแก้ไขปัญหาต่าง ๆ แต่วิธีนี้ใช้ได้ผลดีที่สุดสำหรับฉันบางคนมีข้อบกพร่อง แต่ก็เหมาะสำหรับฉัน

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

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

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

1
Wieslaw Olborski ยินดีต้อนรับลิงก์ไปยังโซลูชัน แต่โปรดตรวจสอบให้แน่ใจว่าคำตอบของคุณมีประโยชน์หากไม่มี: เพิ่มบริบทรอบลิงก์เพื่อให้ผู้ใช้เพื่อนของคุณมีความคิดว่ามันคืออะไรและเพราะเหตุใด หน้าเว็บที่คุณเชื่อมโยงไปถึงในกรณีที่หน้าเป้าหมายไม่พร้อมใช้งาน คำตอบที่น้อยกว่าลิงก์อาจถูกลบ
เสียงบี๊ปสองครั้งใน

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

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

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

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

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

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

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

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

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

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

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

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


                }

            }

3
คุณภาพต่ำจริงๆ 1) โหมด ECB (ซึ่งไม่เกี่ยวข้องกับ IV) 2) 3DES 3) สับสนคีย์และรหัสผ่าน 4) การตั้งชื่อไม่ถูกต้อง 5) ไม่มี MAC
CodesInChaos

-2

เพื่อความง่ายฉันทำเพื่อตัวเองฟังก์ชั่นนี้ที่ฉันใช้เพื่อจุดประสงค์ที่ไม่ใช่ crypto: แทนที่ "yourpassphrase" ด้วยรหัสผ่านของคุณ ...

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

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

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

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

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

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

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


}

}


4
1) ไม่มีเกลือในการได้มาซึ่งคีย์ 2) ค่าคงที่ IV ซึ่งพลาดจุดรวมของ IV ควรแตกต่างกันสำหรับการเข้ารหัสแต่ละครั้ง 3) ไม่มีการรับรองความถูกต้อง => ออราเคิล padding เป็นภัยคุกคาม 4) encryptor.TransformFinalBlockนั้นง่ายกว่าการใช้หน่วยความจำและสตรีม crypto
CodesInChaos

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

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

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

15
โหมดการเข้ารหัส ECB ไม่ใช่เรื่องใหญ่หรือไม่?
John Bubriski

4
ใช่ ECB เป็นตัวเลือกที่ปลอดภัยที่สุด ดูความคิดเห็นของ MS: "สำคัญ: ไม่แนะนำให้ใช้โหมดนี้เนื่องจากจะเป็นการเปิดประตูสำหรับการหาช่องโหว่ด้านความปลอดภัยหลายรูปแบบ" msdn.microsoft.com/en-us/library/…
Rich

-3

ฉันต้องการให้คุณมีส่วนร่วมกับรหัสของฉันสำหรับ AES Rfc2898DeriveBytes( ที่นี่คือเอกสารประกอบ) algorhytm เขียนใน C # (. NET Framework 4) และทำงานอย่างเต็มที่กับแพลตฟอร์มที่ จำกัด เช่น. NET Compact Framework สำหรับ Windows Phone 7.0+ (ไม่ใช่ทั้งหมด แพลตฟอร์มรองรับทุกวิธี criptographic ของ. NET Framework!)

ฉันหวังว่านี่จะช่วยได้ทุกคน!

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

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


    public static string Encrypt(this string text, string salt)
    {
        try
        {
            using (Aes aes = new AesManaged())
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                aes.IV = aes.Key;
                using (MemoryStream encryptionStream = new MemoryStream())
                {
                    using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        byte[] cleanText = Encoding.UTF8.GetBytes(text);
                        encrypt.Write(cleanText, 0, cleanText.Length);
                        encrypt.FlushFinalBlock();
                    }

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


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

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

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


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

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


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

10
1) เหตุใดคุณจึงใช้ตัวแปรที่เรียกว่าIVaไม่ใช่ IV แต่เป็นรหัสผ่าน 2) ทำไมคุณถึงตั้งค่า IV = Key คุณต้องสุ่ม IV ใหม่สำหรับการเข้ารหัสแต่ละครั้ง 3) การขาด MAC ช่วยให้สามารถโจมตีช่องว่างภายในได้
CodesInChaos

-4

คุณต้องใช้เนมสเปซโดยใช้ System.Security.Cryptography และ useHashing เป็นประเภทบูลทั้งจริงหรือเท็จ ตัวแปรสตริง "คีย์" ควรเหมือนกันสำหรับการเข้ารหัสและสำหรับการถอดรหัส

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5
-1 1) โหมด ECB อ่อนแอมาก 2) การขาด MAC ทำให้คุณเปิดการโจมตีที่แอคทีฟเช่นออราเคิล padding 3) ทำไมคุณยังคงใช้ 3DES ทุกวันนี้ มันไม่แตก แต่ AES เป็นตัวเลือกที่ดีกว่าอย่างชัดเจน
CodesInChaos

-4

อัลกอริทึมที่ดีในการแฮชข้อมูลอย่างปลอดภัยคือBCrypt :

นอกเหนือจากการผสมผสานเกลือเพื่อป้องกันการโจมตีแบบเรนโบว์เทเบิลแล้ว bcrypt ยังเป็นฟังก์ชั่นการปรับเปลี่ยน: เมื่อเวลาผ่านไปจำนวนการทำซ้ำสามารถเพิ่มขึ้นเพื่อทำให้ช้าลงดังนั้นจึงยังคงทนทานต่อการโจมตีด้วยการค้นหาที่โหดเหี้ยม

มีความสุขคือการดำเนินการ .NET ของ bcryptที่มีอยู่นอกจากนี้ยังเป็นแพคเกจ NuGet


12
คำถามถามถึงวิธีการเข้ารหัสและถอดรหัสสตริง ถ้าฉันไม่มีอะไรใหญ่โตคุณจะถอดรหัสสตริงใน BCrypt ได้อย่างไร? BCrypt แม้จะมีชื่อเป็นฟังก์ชั่นการแฮช
The1nk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.