Programing

C #에서 문자열 암호화 및 암호 해독

lottogame 2020. 3. 6. 08:17
반응형

C #에서 문자열 암호화 및 암호 해독


이 질문에는 이미 답변이 있습니다.

C #에서 다음을 만족시키는 가장 현대적인 방법은 무엇입니까?

string encryptedString = SomeStaticClass.Encrypt(sourceString);

string decryptedString = SomeStaticClass.Decrypt(encryptedString);

그러나 소금, 열쇠, byte []로 뭉치기 등을 포함하는 최소한의 소란이 있습니다.

인터넷 검색을하고 내가 찾은 것에 혼란 스럽습니다 (이것은 물어 보는 사기적인 질문 인 유사한 SO Q 목록을 볼 수 있습니다).


2015 년 12 월 23 일 업데이트 :이 답변에 많은 찬사를 받고있는 것처럼 보이므로 바보 같은 버그를 수정하고 일반적으로 의견과 피드백을 기반으로 코드를 개선하도록 업데이트했습니다. 특정 개선 사항 목록은 게시물 끝을 참조하십시오.

다른 사람들이 말했듯이 암호화는 간단하지 않으므로 "자신의 롤링"암호화 알고리즘을 피하는 것이 가장 좋습니다.

그러나 기본 제공 RijndaelManaged암호화 클래스 와 같은 방식으로 래퍼 클래스를 "롤"할 수 있습니다 .

Rijndael은 현재 고급 암호화 표준 의 알고리즘 이름 이므로 "모범 사례"로 간주 될 수있는 알고리즘을 사용하고 있습니다.

RijndaelManaged클래스는 일반적으로 바이트 배열, 솔트, 키, 초기화 벡터 등을 사용하여 "충돌"해야하지만 실제로는 "래퍼"클래스 내에서 다소 추상화 될 수있는 세부 사항입니다.

다음 클래스는 내가 한 후에 정확히 어떤 종류의 문자열 기반 일반 텍스트를 문자열 기반 암호로 암호화하고 결과적으로 암호화 된 문자열을 사용하여 수행하는 작업을 수행하기 위해 작성한 클래스입니다. 문자열로 표시됩니다. 물론 동일한 비밀번호로 암호화 된 문자열을 해독하는 동등한 방법이 있습니다.

매번 정확히 동일한 소금 및 IV 값을 사용한이 코드의 첫 번째 버전과 달리이 최신 버전에서는 매번 임의의 소금 및 IV 값을 생성합니다. 솔트와 IV는 주어진 문자열의 암호화와 해독 사이에서 동일해야하기 때문에, 솔트와 IV는 암호화시 암호 텍스트 앞에 추가되고 해독을 수행하기 위해 암호 텍스트에서 다시 추출됩니다. 그 결과 동일한 암호를 사용하여 동일한 일반 텍스트를 암호화하면 매번 완전히 다른 암호문 결과가 제공됩니다.

이것을 사용하는 "강점"은 RijndaelManaged클래스를 사용 하여 문자열을 기반으로 표준 및 보안 알고리즘 (특히 PBKDF2 )을 사용하여 암호화 키를 생성 하는 네임 스페이스 Rfc2898DeriveBytes 함수 를 사용하여 암호화를 수행하는 것에서 비롯 됩니다. 사용자가 제공 한 비밀번호. (이것은 첫 번째 버전의 이전 PBKDF1 알고리즘 사용 개선 사항입니다).System.Security.Cryptography

마지막으로이 인증 은 여전히 인증되지 않은 암호화입니다. 암호화만으로 프라이버시 만 제공됩니다 (즉, 메시지는 제 3 자에게 알려지지 않음). 인증 된 암호화는 프라이버시와 진정성을 모두 제공하는 것을 목표로합니다 (즉, 수신자는 메시지가 발신자가 보낸 것으로 알고 있음).

정확한 요구 사항을 알지 못하면 여기의 코드가 사용자의 요구에 충분히 안전한지 말하기는 어렵지만 구현의 상대적 단순성과 "품질"간의 균형이 잘 맞도록 제작되었습니다. 예를 들어, 암호화 된 문자열의 "수신자"가 신뢰할 수있는 "보낸 사람"으로부터 직접 문자열을받는 경우 인증 이 필요하지 않을 수도 있습니다 .

더 복잡한 것이 필요하고 인증 된 암호화를 제공하는 경우이 게시물 에서 구현을 확인하십시오 .

코드는 다음과 같습니다.

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

namespace EncryptStringSample
{
    public static class StringCipher
    {
        // 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 = 256;

        // 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 = Generate256BitsOfRandomEntropy();
            var ivStringBytes = Generate256BitsOfRandomEntropy();
            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 = 256;
                    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 = 256;
                    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[] Generate256BitsOfRandomEntropy()
        {
            var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                // Fill the array with cryptographically secure random bytes.
                rngCsp.GetBytes(randomBytes);
            }
            return randomBytes;
        }
    }
}

위의 클래스는 다음과 유사한 코드로 아주 간단하게 사용할 수 있습니다.

using System;

namespace EncryptStringSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Please enter a password to use:");
            string password = Console.ReadLine();
            Console.WriteLine("Please enter a string to encrypt:");
            string plaintext = Console.ReadLine();
            Console.WriteLine("");

            Console.WriteLine("Your encrypted string is:");
            string encryptedstring = StringCipher.Encrypt(plaintext, password);
            Console.WriteLine(encryptedstring);
            Console.WriteLine("");

            Console.WriteLine("Your decrypted string is:");
            string decryptedstring = StringCipher.Decrypt(encryptedstring, password);
            Console.WriteLine(decryptedstring);
            Console.WriteLine("");

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }
    }
}

간단한 VS2013 샘플 솔루션 (여러 단위 테스트 포함)을 여기에서 다운로드 할 수 있습니다 .

2015 년 12 월 23 일 업데이트 : 코드의 특정 개선 사항 목록은 다음과 같습니다.

  • 암호화와 암호 해독에서 인코딩이 다른 버그를 수정했습니다. salt & IV 값이 생성되는 메커니즘이 변경되었으므로 더 이상 인코딩이 필요하지 않습니다.
  • salt / IV 변경으로 인해 16 자 문자열의 UTF8 인코딩에서 32 바이트를 생성한다고 잘못 표시 한 이전 코드 주석은 더 이상 적용 할 수 없습니다 (인코딩이 더 이상 필요하지 않음).
  • 대체 된 PBKDF1 알고리즘의 사용이 최신 PBKDF2 알고리즘의 사용으로 대체되었습니다.
  • 암호 파생은 이제 제대로 소금에 절인 반면 이전에는 전혀 소금에 절이지 않았습니다 (또 다른 바보 같은 버그가 박살났습니다).

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

public static class EncryptionHelper
{
    public static string Encrypt(string clearText)
    {
        string EncryptionKey = "abc123";
        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;
    }
    public static string Decrypt(string cipherText)
    {
        string EncryptionKey = "abc123";
        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;
    }
}

이 수업을보십시오 :

public class DataEncryptor
{
    TripleDESCryptoServiceProvider symm;

    #region Factory
    public DataEncryptor()
    {
        this.symm = new TripleDESCryptoServiceProvider();
        this.symm.Padding = PaddingMode.PKCS7;
    }
    public DataEncryptor(TripleDESCryptoServiceProvider keys)
    {
        this.symm = keys;
    }

    public DataEncryptor(byte[] key, byte[] iv)
    {
        this.symm = new TripleDESCryptoServiceProvider();
        this.symm.Padding = PaddingMode.PKCS7;
        this.symm.Key = key;
        this.symm.IV = iv;
    }

    #endregion

    #region Properties
    public TripleDESCryptoServiceProvider Algorithm
    {
        get { return symm; }
        set { symm = value; }
    }
    public byte[] Key
    {
        get { return symm.Key; }
        set { symm.Key = value; }
    }
    public byte[] IV
    {
        get { return symm.IV; }
        set { symm.IV = value; }
    }

    #endregion

    #region Crypto

    public byte[] Encrypt(byte[] data) { return Encrypt(data, data.Length); }
    public byte[] Encrypt(byte[] data, int length)
    {
        try
        {
            // Create a MemoryStream.
            var ms = new MemoryStream();

            // Create a CryptoStream using the MemoryStream 
            // and the passed key and initialization vector (IV).
            var cs = new CryptoStream(ms,
                symm.CreateEncryptor(symm.Key, symm.IV),
                CryptoStreamMode.Write);

            // Write the byte array to the crypto stream and flush it.
            cs.Write(data, 0, length);
            cs.FlushFinalBlock();

            // Get an array of bytes from the 
            // MemoryStream that holds the 
            // encrypted data.
            byte[] ret = ms.ToArray();

            // Close the streams.
            cs.Close();
            ms.Close();

            // Return the encrypted buffer.
            return ret;
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
        }
        return null;
    }

    public string EncryptString(string text)
    {
        return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(text)));
    }

    public byte[] Decrypt(byte[] data) { return Decrypt(data, data.Length); }
    public byte[] Decrypt(byte[] data, int length)
    {
        try
        {
            // Create a new MemoryStream using the passed 
            // array of encrypted data.
            MemoryStream ms = new MemoryStream(data);

            // Create a CryptoStream using the MemoryStream 
            // and the passed key and initialization vector (IV).
            CryptoStream cs = new CryptoStream(ms,
                symm.CreateDecryptor(symm.Key, symm.IV),
                CryptoStreamMode.Read);

            // Create buffer to hold the decrypted data.
            byte[] result = new byte[length];

            // Read the decrypted data out of the crypto stream
            // and place it into the temporary buffer.
            cs.Read(result, 0, result.Length);
            return result;
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
        }
        return null;
    }

    public string DecryptString(string data)
    {
        return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data))).TrimEnd('\0');
    }

    #endregion

}

다음과 같이 사용하십시오.

string message="A very secret message here.";
DataEncryptor keys=new DataEncryptor();
string encr=keys.EncryptString(message);

// later
string actual=keys.DecryptString(encr);

암호를 메모리에 저장해야하고 암호화하려면 SecureString 을 사용해야합니다 .

http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx

좀 더 일반적인 용도로는 Rijndael으로 알려진 Advanced Encryption Standard와 같은 FIPS 승인 알고리즘을 사용합니다. 구현 예는이 페이지를 참조하십시오.

http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx


RijndaelManaged아직 지원하지 않는 ASP.NET Core를 대상으로하는 경우을 사용할 수 있습니다 IDataProtectionProvider.

먼저 데이터 보호를 사용하도록 애플리케이션을 구성하십시오.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDataProtection();
    }
    // ...
}

그런 다음 IDataProtectionProvider인스턴스 를 주입 하고이를 사용하여 데이터를 암호화 / 복호화 할 수 있습니다.

public class MyService : IService
{
    private const string Purpose = "my protection purpose";
    private readonly IDataProtectionProvider _provider;

    public MyService(IDataProtectionProvider provider)
    {
        _provider = provider;
    }

    public string Encrypt(string plainText)
    {
        var protector = _provider.CreateProtector(Purpose);
        return protector.Protect(plainText);
    }

    public string Decrypt(string cipherText)
    {
        var protector = _provider.CreateProtector(Purpose);
        return protector.Unprotect(cipherText);
    }
}

자세한 내용은 이 기사 를 참조하십시오.


ProtectedData사용자의 로그온 자격 증명을 사용하여 데이터를 암호화 하는 클래스를 찾고있을 수 있습니다 .


암호화를 수행하는 가장 쉬운 방법은 RSA를 사용하는 것입니다.

MSDN을 확인하십시오 : http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx

바이트 사용과 관련이 있지만, 그에 관해서는 암호화 및 암호 해독이 어려운 것을 알아 내기를 원합니다. 그렇지 않으면 해킹하기 쉽습니다.

참고 URL : https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp



반응형