C #에서 문자열을 암호화하고 해독합니까?


695

C #에서 문자열을 어떻게 암호화하고 해독 할 수 있습니까?


2

3
간단한 것이 필요했습니다 ...이 링크는 저에게 효과적이었습니다. saipanyam.net/2010/03/encrypt-query-strings.html
— MrM

6
3DES를 삭제하고 AES-GCM을 사용하는 것이 좋습니다. AES-GCM은 .NET 4.5 암호화 라이브러리에서 찾을 수 없으며 'usual AES'(= 보통 AES-CBC 모드)와 다릅니다. AES-GCM은 내가 들어 가지 않을 암호상의 이유로 '일반적인'AES보다 훨씬 낫습니다. 그래서 jbtule이 아래 아래 가장 좋은 대답이 Bouncy Castle AES-GCM하위 섹션을. 당신은 우리를 믿지 않는 경우에, 최소한의 신뢰에서 NSA의 전문가 (NSA 스위트 B @ nsa.gov/ia/programs/suiteb_cryptography/index.shtml : The Galois/Counter Mode (GCM) is the preferred AES mode.)
— DeepSpace101

1
@Sid 개인적으로 대부분의 상황에서 AES-GCM보다 AES-CBC + HMAC-SHA2를 선호합니다. nonce를 재사용하면 GCM이 실패합니다.
— 코드 InChaos

2
@Sid Nonce 재사용은 나쁜 생각입니다. 그러나 유능한 프로그래머 / 암호화 전문가들조차도 이런 일이 발생하는 것을 보았습니다. 이 경우 GCM이 완전히 분해되는 반면 CBC + HMAC는 약간의 약점 만 발생합니다. 프로토콜과 같은 SSL을 사용하면 GCM은 훌륭하지만 표준 "암호화 및 인증"API로는 마음에 들지 않습니다.
— 코드 InChaos

답변:


415

2013 년 10 월 편집 : 단점을 해결하기 위해 시간이 지남에 따라이 답변을 편집했지만 더 강력하고 정보가 풍부한 솔루션에 대한 jbtule의 답변 을 참조하십시오 .

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

원래 답변 :

다음은 "RijndaelManaged Class"문서 및 MCTS Training Kit 에서 파생 된 실제 예제 입니다.

EDIT 2012-April :이 답변은 jbtule의 제안에 따라 IV를 미리 추가하고 다음과 같이 편집되었습니다.

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하려면-귀하의 예를 들어 안녕. Maybye one 생각-키 leng에 문제가 있음-MD5로 수정 했으므로 누군가가 기능 pls에서 예제를 사용하면 키 정규화에 이것을 사용하십시오 (또는 다른 해시 알고리즘을 사용할 수 있습니다 : HashAlgorithm hash = new MD5CryptoServiceProvider (); UnicodeEncoding UE = 새로운 UnicodeEncoding (); 바이트 [] 키 = hash.ComputeHash (UE.GetBytes (encrypt_password)); 내 영어에 대한 ps : sorry :) slinti

18
위의 코드는 안전하지 않으며 aes로 가장 기본적인 의미 보안 규칙을 위반하므로 동일한 키로 동일한 IV를 두 번 이상 사용해서는 안됩니다. 이것은 동일한 키를 사용할 때마다 항상 동일한 IV를 제공합니다.
— jbtule

7
열쇠 추출 과정에서 소금을 사용하는 것은 아프지 않습니다. 상수가 좋은 IV가 아닌 것처럼 상수는 좋은 소금이 아닙니다.
— 코드 InChaos

5
AES 대 Rijndael 혼란에 관하여 : AES는 Rijndael의 하위 집합입니다. 128 비트 블록과 128, 192 또는 256 비트 키와 함께 Rijndael을 사용하는 경우 AES를 사용하고 있습니다.
— 코드 InChaos

3
소금은 균열을 방지하기 위해 난독 화 정도를 추가합니다. 소금이 생성되는 아래의 jbtules 예제를 읽는 것이 좋습니다.
— Brett

360

문자열의 대칭 인증 암호화에 대한 최신 예

대칭 암호화의 일반적인 모범 사례는 AAE (Authentiticated Encryption with Associated Data)를 사용하는 것이지만 표준 .net 암호화 라이브러리의 일부는 아닙니다. 따라서 첫 번째 예는 AES256 을 사용한 다음 HMAC256을 사용합니다.이 단계는 Encrypt 다음 MAC 두 단계 로 더 많은 오버 헤드와 더 많은 키가 필요합니다.

두 번째 예는 오픈 소스 탄력성 (nuget을 통해)을 사용하는 AES256- GCM 의 간단한 연습을 사용합니다.

두 예제 모두 비밀 메시지 문자열, 키 및 선택적인 비 비밀 페이로드 및 선택적으로 비 비밀 데이터 앞에 추가 된 인증 및 암호화 된 암호화 문자열을 취하는 기본 기능을 가지고 있습니다. 이상적으로 무작위로 생성 된 256 비트 키와 함께 이것을 사용하는 것이 좋습니다 NewKey().

두 예제 모두 문자열 암호를 사용하여 키를 생성하는 도우미 메서드가 있습니다. 이러한 도우미 메서드는 다른 예제와 비교할 수 있도록 편의상 제공되지만 암호 강도가 256 비트 키보다 훨씬 약하기 때문에 보안 수준 이 훨씬 떨어 집니다.

업데이트 :byte[] 과부하가 추가 되었으며 스택 오버 플로우 응답 제한으로 인해 Gist 만 4 개의 공백 들여 쓰기 및 API 문서로 전체 형식이 지정되었습니다.


.NET 내장 암호화 (AES) -Then-MAC (HMAC) [Gist]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      byte[] cipherText;
      byte[] iv;

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

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

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

          cipherText = cipherStream.ToArray();
        }

      }

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

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

    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      byte[] cryptKey;
      byte[] authKey;

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

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

탄력이 성 AES-GCM [Gist]

/*
 * 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
좋은 질문입니다. 이들은 Authenticated Encryption 예제를 사용 하고 있습니다. 암호를 암호화 하여 다른 사람이 암호문을 수정하지 않았 음을 검증 하는 MAC 을 가지고 있으며, 이는 주로 선택된 암호문 공격 을 막는 것 입니다. 따라서 암호를 해독 할 때 추가 된 MAC을 확인하여 인증하기 위해 MAC을 계산하고, 인증하는 경우 해독하고 그렇지 않은 경우 null을 반환합니다.
— jbtule

3
MAC의 배열 검사는 모든 색인을 수행합니다. 타이밍 공격을 사용하여 일치하지 않는 첫 번째 바이트를 반환하는 경우 가짜 암호문에서 새 MAC을 계산할 수 있습니다.
— jbtule

5
그것은 좋은 책이며 비교적 최근입니다. 내가 더 추천하는 것은 Dan Boneh 의 무료 온라인 강좌 Cryptography I 입니다. 암호화 사용에 대한 실질적인 기반을 제공하는 정말 좋은 비디오, 정말 좋은 퀴즈 및 정말 좋은 기계 문제도 있습니다. 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 Key를 어디서 구할 수 있는지 또는 어떻게 생성 할 수 있습니까?
— Akash Kava

11
왜 RSA인가? RSA에는 그 용도가 있지만 이것이 그중 하나라는 것을 나타내는 것은 없습니다.
— 코드 InChaos

39
원래의 질문 에서조차도 RSA잘 맞는 징후는 없습니다 . 비대칭 암호화에는 그 용도가 있지만 기본 암호화로 올바른 선택은 아닙니다. RSA 클래스는 범용 암호화 용으로 설계되지 않았으므로 더 긴 문자열에 대해서는 예제 코드가 실패합니다. 비대칭 기능이 필요한 경우 RSA로 대칭 키를 암호화하고 해당 대칭 키로 실제 데이터를 암호화해야합니다. 그래서 나는 여전히 당신의 대답이 나쁜 조언이라고 믿습니다.
— 코드 InChaos

9
CodesInChaos가 이러한 유형의 암호화에 대해 Assymetric이 아닌 대칭 키가 필요하다고 말한 것처럼, 잘못된 답변에 대해 70 표를 받았습니다.
— 오토 카 넬리 스

5
그것은 잘못된 대답이 아니며 엄청난 오버 헤드로 지나치게 복잡해집니다. 더 나은 결과를 얻으려면 AES / 다른 대칭 방법을 사용하십시오.
— Tomer W

54

ASP.Net을 사용하는 경우 이제 .Net 4.0 이상에서 기본 제공 기능을 사용할 수 있습니다.

System.Web.Security.MachineKey

.Net 4.5에는 MachineKey.Protect()및이 MachineKey.Unprotect()있습니다.

.Net 4.0에는 MachineKey.Encode()및이 MachineKey.Decode()있습니다. MachineKeyProtection을 'All'로 설정하면됩니다.

ASP.Net 외부 에서이 클래스는 모든 응용 프로그램을 다시 시작할 때마다 새 키를 생성하는 것처럼 보이므로 작동하지 않습니다. ILSpy를 살짝 들여다 보면 적절한 app.settings가 없으면 자체 기본값을 생성하는 것처럼 보입니다. 따라서 실제로 ASP.Net 외부에서 설정할 수 있습니다.

System.Web 네임 스페이스 외부에서 ASP.Net이 아닌 해당 항목을 찾을 수 없었습니다.


흠 왜이 답변에 투표 수가 적은지 말해 줄 수 있습니까? ASP.NET 응용 프로그램에는 매우 편리한 방법 인 것 같습니다.
— Dirk Boer

@ DirkBoer이 기능은 질문이 제기 된 후 2 년 후에 추가되었습니다.이 질문에 대한 답변을 추가하여 오늘날 사람들에게 더 쉬운 방법이 있음을 알려줍니다. 이것은 또한 app.config-fu가없는 ASP.Net에서만 작동하며 수행중인 작업을 모르는 경우 상당히 위험합니다.
— mattmanser

3
내 무지를 용서하지만 웹 페이지에서 내 대답을 알 수 없습니다. 한 컴퓨터에서 문자열을 암호화하면 데이터베이스에 쓰고 다른 컴퓨터에서 읽습니다. 목적 매개 변수의 값이 같은 한 암호를 해독 할 수 있습니까? 어쩌면 난 그냥 클래스 이름 "MachineKey"에 의해 혼동 해요
— 아드리안 Davel

또 다른 질문은 WPF 앱에서 사용할 수 있습니까? 웹 참조가 없습니다. System.Web에 참조를 추가해도 괜찮습니까?
— Adriaan Davel

2
@AdriaanDavel 링크 된 문서에 따라 "MachineKey API는 ASP.NET 앱에서만 사용해야합니다. ASP.NET 응용 프로그램의 컨텍스트 외부에서 MachineKey API의 동작은 정의되어 있지 않습니다"-게임을 즐기는 경우에만 사용하십시오. 러시안 룰렛
— 마크 Sowul

47

BouncyCastle 은 .NET 용 크립토 라이브러리 로, 프로젝트에 설치하기위한 Nuget 패키지 로 제공됩니다 . System.Security.Cryptography 라이브러리에서 현재 사용 가능한 것보다 훨씬 더 좋습니다. 사용 가능한 알고리즘 측면에서 더 많은 옵션을 제공하고 해당 알고리즘에 대한 더 많은 모드를 제공합니다.

이것은 Bruce Schneier (저기 우리 편집증 사람들의 영웅)에 의해 작성된 TwoFish 구현의 예입니다 . Rijndael (일명 AES)과 같은 대칭 알고리즘입니다. 그것은 AES 표준에 대한 3 개의 결선 참가자 중 한 명이며 브루스 슈나이어 (Bruce Schneier)에 의해 작성된 BlowFish라는 유명한 알고리즘의 형제입니다.

bouncycastle의 첫 번째 방법은 암호화 기 클래스를 만드는 것입니다. 이렇게하면 라이브러리 내의 다른 블록 암호를보다 쉽게 ​​구현할 수 있습니다. 다음 암호화 클래스는 T가 IBlockCipher를 구현하고 기본 생성자를 갖는 일반 인수 T를 사용합니다.

업데이트 : 대중적인 요구로 인해 무작위 IV 생성을 구현 하고이 클래스에 HMAC를 포함하기로 결정했습니다. 비록 스타일 관점에서 이것은 단일 책임의 SOLID 원칙에 위배되지만,이 클래스의 특성상 폐기되었습니다. 이 클래스는 이제 암호와 다이제스트에 대한 두 가지 일반 매개 변수를 사용합니다. RNGCryptoServiceProvider를 사용하여 IV를 자동으로 생성하여 우수한 RNG 엔트로피를 제공하고 BouncyCastle에서 원하는 다이제스트 알고리즘을 사용하여 MAC을 생성 할 수 있습니다.

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

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

    private IBlockCipher blockCipher;

    private BufferedBlockCipher cipher;

    private HMac mac;

    private byte[] key;

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

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

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

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

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

        var iv = this.GenerateIV();

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

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

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

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

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

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

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

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

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

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

        return AreEqual(digest,computed);
    }

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

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

        return result == 0;
    }

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

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

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

            return result;
        }
    }

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

        return result;
    }
}

다음으로 새 클래스에서 encrypt 및 decrypt 메소드를 호출하십시오. twofish를 사용한 예는 다음과 같습니다.

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

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

TripleDES와 같은 다른 블록 암호를 쉽게 대체 할 수 있습니다.

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

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

마지막으로 SHA256 HMAC와 함께 AES를 사용하려면 다음을 수행하십시오.

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

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

암호화에서 가장 어려운 부분은 실제로 알고리즘이 아닌 키를 처리하는 것입니다. 키를 어디에 저장해야하는지, 필요한 경우 키를 어떻게 교환해야하는지 고려해야합니다. 이 알고리즘은 모두 시간 테스트를 견뎌냈으며 깨지기가 매우 어렵습니다. 정보를 훔치려는 사람은 메시지에서 암호 분석을 수행하는 데 영원을 쓰지 않을 것이며 키가 무엇인지 또는 어디에 있는지 알아 내려고 시도 할 것입니다. 따라서 # 1을 현명하게 선택하고 # 2 안전한 장소에 저장하십시오 .web.config와 IIS를 사용하는 경우 web.config의 일부를 암호화 할 수 있으며 마지막으로 키를 교환 해야하는 경우 키 교환을위한 프로토콜이 안전합니다.

업데이트 2 타이밍 공격을 완화하기 위해 비교 방법을 변경했습니다. 자세한 정보는 여기 http://codahale.com/a-lesson-in-timing-attacks/를 참조 하십시오 . 또한 기본적으로 PKCS7 패딩으로 업데이트되었으며 최종 사용자가 사용할 패딩을 선택할 수있는 새로운 생성자를 추가했습니다. 제안에 대해 @CodesInChaos에게 감사드립니다.


3
1) 그 클래스는 IV 관리의 부담을 사용자에게 거의 확실하게 알려주기 때문에 사용하기가 상당히 성가시다. 2) MAC 부족으로 인해 패딩 오라클에 취약합니다.
— 코드 InChaos

3
1) 패딩이 나에게 부서진 것처럼 보입니다. 제로 패딩을 추가하고 제거하지 마십시오. 제로 패딩은 안정적으로 제거 할 수 없으므로 나쁜 생각입니다. 대신 PKCS # 7 패딩을 사용하십시오. bouncycastle 암호화 / 암호 해독 기능이 이미 이것을 지원할 것으로 기대합니다. 2) 일정 시간 비교를 사용하여 MAC의 유효성을 검사해야합니다 SequenceEqual. 이것은 제시된 MAC의 프리픽스와 실제 MAC가 얼마나 오랫동안 일치 하는지를 누설하는 타이밍 사이드 채널을 피한다.
— 코드 InChaos

2
@CodesInChaos 동의합니다. 이것을 확인해 주셔서 감사합니다. 이러한 커플 문제를 해결하기 위해 편집했습니다. – nerdybeardo
— nerdybeardo

큰 대답, 단 하나의 질문 .... 열쇠와 hmacKey는 무엇입니까, 나는 crypto를 처음 사용합니다 .. 감사합니다!
— Terkhos

1
@Terkhos RNGCryptoServiceProvider와 같은 키를 생성하려면 안전한 난수 생성기를 사용해야합니다. 암호문이나 예측 가능한 것을 사용해서는 안됩니다. 또한 알고리즘에서 제공하는 최대 길이를 사용해야합니다. 예를 들어 AES 256은 길이가 256 비트 인 키 크기를 사용하므로 32 개의 임의 바이트가 가장 좋으며, HMAC 키 크기는 일반적으로 SHA2와 같은 알고리즘의 크기를 기반으로합니다 ( 256) 안전한 난수 생성기에 의해 생성 된 256 비트 키로 충분합니다. 키를 자주 바꾸십시오! 더 자주 더 좋습니다!
— nerdybeardo 1

18

고지 사항 :이 솔루션은 일반에게 공개되지 않은 유휴 데이터 (예 : 구성 파일 또는 DB)에만 사용해야합니다. 이 시나리오에서만 유지 보수가 적기 때문에 빠르고 더러운 솔루션을 @jbtule 솔루션보다 더 나은 것으로 간주 할 수 있습니다.

원래 게시물 : jbtule 의 답변이 빠르고 더러운 AES 문자열 암호화에 약간 복잡 하다는 것을 알았 으며 Brett 의 답변에는 Initialization Vector가 고정 값으로 버그가있어 패딩 공격에 취약하므로 Brett의 코드를 수정했습니다. 부서진 문자열에 추가되는 임의의 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)동일할까요?
— dub stylee

"같은"은 무슨 뜻입니까? 암호 해독 기능은 암호화 기능에 의해 생성 된 모든 값을 가져 와서 초기화 벡터와 암호화 된 값으로 분리하여 해독합니다.
— Gil Cohen

1
Encrypt매번 다른 IV 가 생성되는 것을 보지 못했습니다 . 어떤 이유로 나는 IV가 매번 동일하다고 생각했기 때문에 기본적으로 무의미합니다.
— dub stylee

1
@GilCohen 글쎄, 여기에 큰 면책 조항을 작성하고 유휴 데이터에 대해서만 사용하고 서비스에 노출하지 말고 위험 관리를 청구 할 수 있다고 말합니다. 그러나 , 당신의 빠르고 더티는 너무 조잡합니다. 예를 들어 암호 해독에서 공백을 더하기 기호로 바꾸고 그 반대의 경우는 바꾸지 않는 이유는 무엇입니까? URL 쿼리 문자열, 쿠키 또는 양식 변수 hmm을 통해 전달하는 것처럼 서비스처럼 들리며 암호 텍스트 를 인증 해야 할 때 절대적으로 필요 합니다.
— jbtule

2
@jbtule 실제로 아니오, 이것은 어떤 이유로 Base64 함수의 인코딩입니다. 이것은 실제로 유휴 데이터에 사용되었으며 귀하의 의견에 동의합니다. 추가하겠습니다.
— 길 코헨

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이 아닌 2 진입니다. 키가 ASCII 문자로 구성되어 있으면 (실제로) 유효 키 크기를 48 비트로 줄입니다. 3) 각 메시지마다 IV가 달라야합니다. 4) MAC 부족으로 인해 패딩 오라클을 포함한 적극적인 공격에 노출 될 수 있습니다.
— 코드 InChaos

9
+1 OP는 최대 강도를 요구하지 않고 매우 간단한 질문을했으며이 답변은 완벽하게 해당됩니다. 암호화에 간단한 사용법도 있기 때문에 적어도 이것을 사용할 수 있습니다.
— Roland

코드에서 언급 한대로 -1 @Roland IV는 각 메시지마다 달라야합니다. 매우 간단하게 사용하지 않는 경우 API를 잘못 사용하므로이 코드를 사용해서는 안됩니다. 기간. 48 비트 키를 어둡게하지 않으면 단 하루 만에 키가없는 사람은이 암호를 해독 할 수 있으므로 더 이상 암호화되지 않으므로 질문에 대답하지 않습니다.
— jbtule 2016 년

@ 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 Salt와 IV, 그리고 그것들을 위해 ASCII 표현을 사용하면, 이것은 모든 종류의 잘못입니다.
— jbtule

7
보안 경고 :이 코드를 사용하지 마십시오 위의 내 의견을 참조하십시오.
— jbtule

6
나는 이것을 철자하지 않은 것에 대해 사과드립니다. IV는 열쇠가 아니며 비밀로 유지하면 보안이 전혀 추가되지 않으며 예측 가능하게 만드는 것은 약간의 보안을 잃습니다. IV를 하드 코딩하는 것은 실제로 AES-CBC 암호화를 사용하는 방법을 알고있는 사람에게는 전혀 불합리하거나 비논리적이며 잘못되었습니다. Encoding.ASCII.GetBytes인간이 선택하는 것이 무엇인가에 엔트로피 추가하기위한 것입니다 데이터, 방법 미만 예상하고보다 엔트로피가 될 것입니다 매우 초보자의 실수. 이것들은 쉽게 수정할 수있는 모든 것이지만 보안상의 의미로 인해 대담한 경고가 유지되지 않습니다.
— jbtule

4
라훌, 진정해! @jbtule의 3 가지 의견이 모두 투표권을 얻은 이유를 생각해보십시오. 그는 당신을 올바른 길로 인도하기 위해 합리적인 이야기를하고 있습니다. 기분이 상하지 않습니다. 당신은 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 안전 값을 암호화 / 암호 해독하는 예입니다.

앞에서 언급했듯이 Machine config 설정 ( 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- 참조). 더 - IIS 매니저 / ) 또는 같은 온라인 시스템 키 생성기를 사용할 수 있습니다 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

다음은 임의의 IV 및 HMAC와 암호 파생 키가있는 AES CBC 모드를 사용하여 C #에서 문자열을 암호화하는 간단한 예이며 기본 이동 부분을 보여줍니다.

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) 20 바이트 이상의 PBKDF2-HMAC-SHA-1을 사용하고있어 공격자의 속도를 늦추지 않고 KDF를 2 배나 느리게합니다
— CodeInChaos

1
@CodesInChaos : 1) 이것은 사람들을 시작하기위한 간단한 예입니다. 저는 명확성을 위해서만 무작위 소금을 생략했습니다. 그러나 좋은 지적입니다. 2) 좋고 미묘한 점. 3) 2 개의 16 바이트 키를 20 바이트로 유도하는 것이 무엇을 제안합니까?
— Jim Flood

가장 간단한 방법은 SHA-2를 사용하여 느린 해시의 출력을 해시하는 것입니다. 더 멋진 방법은 HKDF이거나 단순히 PBKDF2를 다시 적용하는 것이지만 이번에는 반복이 1로 설정되었습니다.
— CodesInChaos

@CodesInChaos SHA-2를 사용하지 않습니다. 해시 함수의 작업은 키 파생 함수의 작업과 다릅니다. 해시는 예측할 수없고 입력이 변경 될 때만 변경하면됩니다. 키는 무작위와 구별 할 수 없어야합니다. KDF에서 32 바이트를 계속 그릴 것입니다. 이 경우 너무 일찍 최적화하고 위험을 추가하고 있습니다.
— Jim Flood

3

AES-GCM 암호화를 위한 BouncyCastle의 대안 은 libsodium-net 입니다. libsodium C 라이브러리를 래핑합니다. 한 가지 장점은 매우 빠른 암호화를 위해 CPU에서 AES-NI 확장을 사용한다는 것입니다. 단점은 CPU에 확장명이 없으면 전혀 작동하지 않는다는 것입니다. 대체되는 소프트웨어가 없습니다.


3

다음 코드는 비슷한 질문에 대한 가잘의 답변 을 개선 한 버전입니다 .

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

이것은 Brett에 의해 여기에 배치 된 수업입니다. 그러나 URL 문자열을 사용하여 암호화하고 해독 할 때 'Base-64 char 배열의 길이가 잘못되었습니다'라는 오류가 발생하여 약간 편집했습니다.

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

암호화는 프로그래밍에서 매우 일반적인 문제입니다. 작업을 수행하기 위해 패키지를 설치하는 것이 좋습니다. 어쩌면 Simple Aes Encryption 과 같은 간단한 오픈 소스 Nuget 프로젝트

키는 구성 파일에 있으므로 프로덕션 환경에서 쉽게 변경할 수 있으며 단점이 없습니다.

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

1
큰 단점은 인증 된 암호화가 아니라는 것입니다.
— jbtule

0

비슷한 질문 에서 내 대답에 복사했습니다 : C # 용 간단한 양방향 암호화 .

여러 답변과 의견을 바탕으로합니다.

  • 암호화 텍스트 앞에 붙은 임의의 초기화 벡터 (@jbtule)
  • MemoryStream (@RenniePet) 대신 TransformFinalBlock () 사용
  • 재난을 복사하여 붙여 넣는 사람을 피하기 위해 미리 채워진 키가 없음
  • 적절한 폐기 및 패턴 사용

암호:

/// <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을 추가해야합니다.
— 코드 InChaos

당신은 아마 맞습니다, 나는이 분야에 능숙하지 않습니다. 처음에이 주제를 방문했을 때 나는 효과적이고 상당히 안전한 간단한 것을 원했습니다. 매우 민감한 데이터를 위해 시도되고 진정한 라이브러리를 사용하고 있습니다.
— angularsen

0

다음은 ASP 스 니펫에서 원래 간단한 스 니펫입니다.

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


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

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

1
무결성 / 인증을 확인하지 않습니다. MAC을 추가해야합니다.
— Artjom B.

실제로 위의 예는 문자열 변수를 암호화 / 암호 해독하는 것입니다.
— Vijay Kumbhoje

3
패딩 오라클 공격으로부터 보호하기 위해 암호화 텍스트를 인증해야합니다 (예 : HMAC로). 이 코드를 다시 살펴보면 의미 적으로 안전하지 않기 때문에 절대 사용해서는 안되는 ECB 모드를 사용하고있는 것 같습니다. 또한 마스터 키와 소금에서 키와 IV를 파생시킬 때 소금은 정적입니다. 이것은 정적 인 IV로 이어져 IV의 전체 개념을 어 기고 체계적으로 의미 론적으로 다시 안전하지 않게 만듭니다.
— Artjom B.

고마워요 브라더, 여기에 올바른 코드를 전달하면 큰 도움이 될 것입니다.
— 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) 암호문에 대한 인증이 없으므로 공격자는이를 감지하지 않고 암호문을 조작 할 수 있습니다 (패딩 오라클 공격 참조).
— Artjom B.

하이 @ArtjomB. 개발자는 암호화 된 문자열과 함께 생성 및 추가되므로 IV 관리에 대해 걱정할 필요가 없습니다.
— Skull

동의하지 않아야합니다. IV는 _iv클래스 변수에 저장되며 암호문에 기록되지 않습니다 . 그러면 수신기가 키와 IV를 어떻게 알 수 있다고 생각합니까? 그것들은 다른 방법으로 배포되어야 할 것입니다. IV는 비밀이 아니므로 각 암호화에 대해 무작위로 생성되어 암호문과 함께 배포되어야합니다.
— Artjom B.


1
1) 위의 링크에서, iv는 문자열과 함께 암호화되기 때문에 iv 관리에 대해 걱정할 필요없이 aes를 구현하는 방법을 얻을 수 있습니다. 2) 참조하는 기능은 개인 액세스 수정자를 포함하므로 외부에서 호출 할 수 없습니다. 암호화하기 위해 Cryptographyclass.Encrytion ( "SAMPLEstring") 함수 만 사용할 수 있습니다
— Skull

0

탄력성 패키지를 사용하여 AES-GCM 암호화 / 암호 해독을 수행하는 방법 은 다음과 같습니다 .

GOlang crypto/aesapi 에서 데이터를 해독 할 가능성이있는 경우 Google에서 샘플을 찾았습니다 .

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

BouncyCastle과 함께 PGPCore 를 사용하여 이를 수행하는 좋은 예 , 매우 간단한 솔루션 : https://blog.bitscry.com/2018/07/05/pgp-encryption-and-decryption-in-c/

나는 다른 해결책을 시도했지만 이것은 나에게 가장 효과적이며, 일부는 버그가 있지만 이것은 나에게 완벽합니다.

using (PGP pgp = new PGP())
{
// Generate keys
pgp.GenerateKey(@"C:\TEMP\keys\public.asc", @"C:\TEMP\keys\private.asc", "email@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 없음
— CodeInChaos

-2

간단하게 내가 비 암호 목적으로 사용하는이 기능을 직접 만들었습니다 : "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) 인증 없음 => 패딩 오라클은 위협입니다. 4) encryptor.TransformFinalBlock메모리와 암호화 스트림을 사용하는 것보다 간단합니다.
— 코드 InChaos

-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/ko-kr/library/…
— Rich

-3

C # (. NET Framework 4)으로 작성되고 Windows Phone 7.0+ 용 .NET Compact Framework (일부 아님)와 같은 제한된 플랫폼에서도 완벽하게 작동하는 AES 코드 Rfc2898DeriveBytes( 여기서는 문서) algorhytm에 대한 코드를 제공하고 싶습니다 . 플랫폼은 .NET 프레임 워크의 모든 암호화 방법을 지원합니다!).

이것이 누군가를 도울 수 있기를 바랍니다!

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) 왜 IVaIV가 아닌 암호 라는 변수를 사용 합니까? 2) 왜 IV = Key를 설정합니까? 각 암호화마다 새로운 임의 IV가 필요합니다. MAC 3) 부족 패딩 오라클 공격을 할 수 있습니다
— CodesInChaos

-4

System.Security.Cryptography를 사용하여 네임 스페이스를 사용해야합니다. useHashing은 부울 유형으로 true 또는 false입니다. 문자열 변수 "key"는 암호화 및 암호 해독에 동일해야합니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5
-1 1) ECB 모드가 매우 약합니다. 2) MAC 부족으로 패딩 오라클과 같은 공격에 노출됩니다. 3) 왜 오늘날에도 여전히 3DES를 사용하십니까? 그것은 깨지지 않았지만 AES는 분명히 더 나은 선택입니다.
— 코드 InChaos

-4

데이터를 안전하게 해시하는 좋은 알고리즘은 BCrypt입니다 .

레인보우 테이블 공격으로부터 보호하기 위해 솔트를 통합하는 것 외에도 bcrypt는 적응 형 기능입니다.

Nury 패키지로 도 제공 되는 BCrypt 의 멋진 .NET 구현 이 있습니다 .


12
질문은 문자열을 암호화하고 해독하는 방법을 묻습니다. 내가 큰 것을 놓치지 않는 한 BCrypt에서 문자열을 어떻게 해독 할 수 있습니까? BCrypt는 이름에도 불구하고 해싱 함수입니다.
— The1nk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.