在 C# 中加密和解密字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10168240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 12:42:45  来源:igfitidea点击:

Encrypting & Decrypting a String in C#

c#encryption

提问by Richard

What is the most modern (best) way of satisfying the following in C#?

在 C# 中满足以下条件的最现代(最佳)方法是什么?

string encryptedString = SomeStaticClass.Encrypt(sourceString);

string decryptedString = SomeStaticClass.Decrypt(encryptedString);

BUT with a minimum of fuss involving salts, keys, mucking about with byte[], etc.

但是,在涉及盐、密钥、字节 [] 等方面的麻烦最少。

Been Googling and confused at what I'm finding (you can see the list of similar SO Qs to see this is a deceptive question to ask).

一直在谷歌上搜索并对我发现的东西感到困惑(你可以查看类似的 SO Q 列表,看看这是一个欺骗性的问题)。

采纳答案by CraigTP

UPDATE 23/Dec/2015: Since this answer seems to be getting a lot of upvotes, I've updated it to fix silly bugs and to generally improve the code based upon comments and feedback. See the end of the post for a list of specific improvements.

2015 年 12 月 23 日更新:由于这个答案似乎得到了很多赞成,我已经对其进行了更新,以修复愚蠢的错误并根据评论和反馈对代码进行总体改进。有关具体改进的列表,请参阅文章末尾。

As other people have said, Cryptography is not simple so it's best to avoid "rolling your own" encryption algorithm.

正如其他人所说,密码学并不简单,因此最好避免“滚动您自己的”加密算法。

You can, however, "roll your own" wrapper class around something like the built-in RijndaelManagedcryptography class.

但是,您可以围绕内置RijndaelManaged密码学类“滚动您自己的”包装类。

Rijndael is the algorithmic name of the current Advanced Encryption Standard, so you're certainly using an algorithm that could be considered "best practice".

Rijndael 是当前Advanced Encryption Standard的算法名称,因此您肯定在使用一种可以被视为“最佳实践”的算法。

The RijndaelManagedclass does indeed normally require you to "muck about" with byte arrays, salts, keys, initialization vectors etc. but this is precisely the kind of detail that can be somewhat abstracted away within your "wrapper" class.

RijndaelManaged班确实通常需要你“关于渣土”使用字节数组,盐,钥匙,初始化向量等,但这恰恰是一种具体的,可有些抽象掉你的“包装”类中。

The following class is one I wrote a while ago to perform exactly the kind of thing you're after, a simple single method call to allow some string-based plaintext to be encrypted with a string-based password, with the resulting encrypted string also being represented as a string. Of course, there's an equivalent method to decrypt the encrypted string with the same password.

下面的类是我前段时间写的,用来执行你所追求的那种事情,一个简单的单一方法调用,允许使用基于字符串的密码加密一些基于字符串的明文,生成的加密字符串也被表示为一个字符串。当然,还有一种等效的方法可以使用相同的密码解密加密字符串。

Unlike the first version of this code, which used the exact same salt and IV values every time, this newer version will generate random salt and IV values each time. Since salt and IV must be the same between the encryption and decryption of a given string, the salt and IV is prepended to the cipher text upon encryption and extracted from it again in order to perform the decryption. The result of this is that encrypting the exact same plaintext with the exact same password gives and entirely different ciphertext result each time.

与此代码的第一个版本每次都使用完全相同的盐和 IV 值不同,这个新版本每次都会生成随机的盐和 IV 值。由于给定字符串的加密和解密之间的 salt 和 IV 必须相同,因此在加密时将 salt 和 IV 附加到密文并再次从中提取以执行解密。这样做的结果是使用完全相同的密码加密完全相同的明文每次都会给出完全不同的密文结果。

The "strength" of using this comes from using the RijndaelManagedclass to perform the encryption for you, along with using the Rfc2898DeriveBytesfunction of the System.Security.Cryptographynamespace which will generate your encryption key using a standard and secure algorithm (specifically, PBKDF2) based upon the string-based password you supply. (Note this is an improvement of the first version's use of the older PBKDF1 algorithm).

使用它的“强度”来自使用RijndaelManaged该类为您执行加密,以及使用命名空间的Rfc2898DeriveBytes函数,该函数System.Security.Cryptography将使用基于字符串的标准和安全算法(特别是PBKDF2)生成您的加密密钥 -基于您提供的密码。(请注意,这是对第一个版本使用旧 PBKDF1 算法的改进)。

Finally, it's important to note that this is still unauthenticatedencryption. Encryption alone provides only privacy (i.e. message is unknown to 3rd parties), whilst authenticated encryption aims to provide both privacy and authenticity (i.e. recipient knows message was sent by the sender).

最后,需要注意的是,这仍然是未经身份验证的加密。单独的加密仅提供隐私(即第三方不知道消息),而经过身份验证的加密旨在提供隐私和真实性(即收件人知道消息是由发件人发送的)。

Without knowing your exact requirements, it's difficult to say whether the code here is sufficiently secure for your needs, however, it has been produced to deliver a good balance between relative simplicity of implementation vs "quality". For example, if your "receiver" of an encrypted string is receiving the string directly from a trusted "sender", then authentication may not even be necessary.

在不知道您的确切要求的情况下,很难说这里的代码是否足以满足您的需求,但是,它的产生是为了在实现的相对简单性与“质量”之间取得良好的平衡。例如,如果加密字符串的“接收者”直接从受信任的“发送者”接收字符串,则甚至可能不需要身份验证。

If you require something more complex, and which offers authenticated encryption, check out this postfor an implementation.

如果您需要更复杂的东西,并且提供经过身份验证的加密,请查看这篇文章以获取实现。

Here's the code:

这是代码:

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

The above class can be used quite simply with code similar to the following:

上面的类可以非常简单地使用类似于以下的代码:

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

(You can download a simple VS2013 sample solution (which includes a few unit tests) here).

(你可以下载一个简单的VS2013样品溶液(其中包括一些单元测试)在这里)。

UPDATE 23/Dec/2015:The list of specific improvements to the code are:

2015 年 12 月 23 日更新:代码的具体改进列表是:

  • Fixed a silly bug where encoding was different between encrypting and decrypting. As the mechanism by which salt & IV values are generated has changed, encoding is no longer necessary.
  • Due to the salt/IV change, the previous code comment that incorrectly indicated that UTF8 encoding a 16 character string produces 32 bytes is no longer applicable (as encoding is no longer necessary).
  • Usage of the superseded PBKDF1 algorithm has been replaced with usage of the more modern PBKDF2 algorithm.
  • The password derivation is now properly salted whereas previously it wasn't salted at all (another silly bug squished).
  • 修复了一个愚蠢的错误,即加密和解密之间的编码不同。由于生成 salt 和 IV 值的机制发生了变化,因此不再需要编码。
  • 由于 salt/IV 更改,之前错误地指示 UTF8 编码 16 个字符串产生 32 个字节的代码注释不再适用(因为不再需要编码)。
  • 被取代的 PBKDF1 算法的使用已被更现代的 PBKDF2 算法的使用所取代。
  • 密码派生现在被正确加盐,而以前它根本没有加盐(另一个愚蠢的错误被压扁了)。

回答by SLaks

You may be looking for the ProtectedDataclass, which encrypts data using the user's logon credentials.

您可能正在寻找ProtectedData使用用户登录凭据加密数据的类。

回答by Mike Calvert

The easiest way that I've seen to do encryption is through RSA

我见过的最简单的加密方法是通过 RSA

Check out the MSDN on it: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx

查看它的 MSDN:http: //msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx

It does involve using bytes, but when it comes down to it you kind of do want encryption and decryption to be tough to figure out otherwise it will be easy to hack.

它确实涉及使用字节,但归根结底,您确实希望加密和解密很难弄清楚,否则很容易被破解。

回答by Ulises

If you need to store a password in memory and would like to have it encrypted you should use SecureString:

如果您需要在内存中存储密码并希望对其进行加密,则应使用SecureString

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

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

For more general uses I would use a FIPS approved algorithm such as Advanced Encryption Standard, formerly known as Rijndael. See this page for an implementation example:

对于更一般的用途,我将使用 FIPS 批准的算法,例如高级加密标准,以前称为 Rijndael。有关实现示例,请参阅此页面:

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

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

回答by ja72

Try this class:

试试这个类:

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('
string message="A very secret message here.";
DataEncryptor keys=new DataEncryptor();
string encr=keys.EncryptString(message);

// later
string actual=keys.DecryptString(encr);
'); } #endregion }

and use it like this:

并像这样使用它:

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

回答by A Ghazal

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

回答by Sergey Kolodiy

If you are targeting ASP.NET Core that does not support RijndaelManagedyet, you can use IDataProtectionProvider.

如果您的目标是尚不支持的 ASP.NET Core,则RijndaelManaged可以使用IDataProtectionProvider.

First, configure your application to use data protection:

首先,配置您的应用程序以使用数据保护:

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

Then you'll be able to inject IDataProtectionProviderinstance and use it to encrypt/decrypt data:

然后你就可以注入IDataProtectionProvider实例并使用它来加密/解密数据:

##代码##

See this articlefor more details.

有关更多详细信息,请参阅此文章