加密&安培;解密在C#中的String [英] Encrypting & Decrypting a String in C#

查看:118
本文介绍了加密&安培;解密在C#中的String的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么是满足在C#以下的最现代化的(最好的)的方式?

 字符串encryptedString = SomeStaticClass.Encrypt(sourceString);字符串decryptedString = SomeStaticClass.Decrypt(encryptedString);

但以最简单的涉及盐,钥匙,摆弄的byte [],等等。

被谷歌搜索,并在我发现什么(你可以看到相似的,所以QS的名单看,这是一个欺骗性的问题要问)混淆。


解决方案

更新23 / DEC / 2015年:由于这个答案似乎得到了很多upvotes,我已经更新了它修复愚蠢的错误和普遍提高基于的意见和反馈,code。看到帖子的具体改进列表的末尾。

至于其他人所说,密码并不简单,所以最好避免滚你自己的加密算法。

您可以,但是,滚你自己的身边像内置的<一个包装类href=\"http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx\"><$c$c>RijndaelManaged加密类。

Rijndael算法是当前高级加密标准的算法名称,所以你肯定使用了一种算法,可以被认为是最佳实践。

的<一个href=\"http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx\"><$c$c>RijndaelManaged类确实如此,通常需要你渣土关于使用字节数组,盐,钥匙,初始化向量等,但这是precisely可有点抽象出来的​​包装类中的那种细节。

下面的类是我写的一个前一阵子执行正是你以后是什么样的事情,简单的一个方法调用,让一些基于字符串的明文与基于字符串的密码进行加密,用得到的加密的字符串也正在psented作为字符串重新$ p $。当然,有解密用相同的密码加密的字符串的等效方法

不同的是code,它使用完全相同的盐和每次IV值的第一个版本,这个新版本将随机生成盐和IV值各一次。因为盐和IV必须是一个给定的字符串,该盐的加密和解密之间的相同和IV是ppended到经加密的密文,并从中提取再次以执行解密$ P $。这样做的结果是,加密具有完全相同的密码完全相同的明文给出每次完全不同的密文导致

在使用这个实力来自使用<$c$c>RijndaelManaged类来执行加密的你,用沿<一个href=\"https://msdn.microsoft.com/en-gb/library/system.security.cryptography.rfc2898derivebytes%28v=vs.110%29.aspx\">Rfc2898DeriveBytes //en.wikipedia:在 System.Security.Cryptography 命名空间,这将产生使用标准和安全算法(特别是加密密钥,的 PBKDF2 )。 (请注意,这是第一个版本的使用老式PBKDF1算法的改进)。

最后,需要注意的是这仍然是的未经验证的加密是很重要的。加密单独只提供保密性(即信息是未知的第三方),而认证加密的目的是提供隐私和真实性(即收件人知道消息是由发送方发送)。

不知道您的具体要求,这是很难说这里的code是否适合您的需要是足够安全的,但是,它一直生产到交付执行相对简单之间的良好平衡与品质。例如,如果你加密的字符串的接收器是直接从一个值得信赖的发件人接到字符串,则验证不得甚至是必要的

而如果你需要更复杂的东西,并提供经过验证的加密,请这个帖子一个实现。

这里的code:

 使用系统;
使用System.Text;
使用System.Security.Cryptography;
使用System.IO;
使用System.Linq的;命名空间EncryptStringSample
{
    公共静态类StringCipher
    {
        //该常数被用来确定在比特加密算法的密钥大小。
        //我们的code之内把这个由8下面来获得字节当量数。
        私人const int的密钥大小= 256;        //此常数决定迭代密码字节生成函数的数量。
        私人const int的Derivati​​onIterations = 1000;        公共静态字符串加密(纯文本字符串,字符串密码)
        {
            //盐和IV在每次随机产生的,而是pre prended加密的密文
            //使得相同盐和IV值可以解密时使用。
            变种saltStringBytes = Generate256BitsOfRandomEntropy();
            变种ivStringBytes = Generate256BitsOfRandomEntropy();
            变种plainTextBytes = Encoding.UTF8.GetBytes(明文);
            使用(VAR密码=新Rfc2898DeriveBytes(密码,saltStringBytes,Derivati​​onIterations))
            {
                VAR keyBytes = password.GetBytes(密钥/ 8);
                使用(VAR symmetricKey =新RijndaelManaged的())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    使用(VAR加密= symmetricKey.CreateEncryptor(keyBytes,ivStringBytes))
                    {
                        使用(VAR的MemoryStream =新的MemoryStream())
                        {
                            使用(VAR的CryptoStream =新的CryptoStream(MemoryStream的,加密,CryptoStreamMode.Write))
                            {
                                cryptoStream.Write(plainTextBytes,0,plainTextBytes.Length);
                                cryptoStream.FlushFinalBlock();
                                //作为随机盐字节,随机四字节和加密字节的级联创建的最后字节。
                                VAR cipherTextBytes = saltStringBytes;
                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray())ToArray的();
                                memoryStream.Close();
                                cryptoStream.Close();
                                返回Convert.ToBase64String(cipherTextBytes);
                            }
                        }
                    }
                }
            }
        }        公共静态字符串解密(密文字符串,字符串密码)
        {
            //获得重新present字节的完整流:
            // [32字节盐] + [32字节的IV] + [n个密文字节]
            VAR cipherTextBytesWithSaltAndIv = Convert.FromBase64String(密文);
            //通过提取从所提供的密文位元组的前32个字节获取saltbytes。
            VAR saltStringBytes = cipherTextBytesWithSaltAndIv.Take(密钥/ 8).ToArray();
            //通过提取从提供的密文字节的下一个32个字节获得四字节。
            VAR ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(密钥/ 8)。取(密钥/ 8).ToArray();
            //从密文形式去除前64个字节得到实际的密文字节。
            变种cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((密钥大小/ 8)* 2)。取(cipherTextBytesWithSaltAndIv.Length - ((密钥大小/ 8)* 2))ToArray的();            使用(VAR密码=新Rfc2898DeriveBytes(密码,saltStringBytes,Derivati​​onIterations))
            {
                VAR keyBytes = password.GetBytes(密钥/ 8);
                使用(VAR symmetricKey =新RijndaelManaged的())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    使用(VAR解密= symmetricKey.CreateDecryptor(keyBytes,ivStringBytes))
                    {
                        使用(VAR的MemoryStream =新的MemoryStream(cipherTextBytes))
                        {
                            使用(VAR的CryptoStream =新的CryptoStream(MemoryStream的,解密,CryptoStreamMode.Read))
                            {
                                VAR plainTextBytes =新的字节[cipherTextBytes.Length]
                                变种decryptedByteCount = cryptoStream.Read(plainTextBytes,0,plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                返回Encoding.UTF8.GetString(plainTextBytes,0,decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }        私人静态的byte [] Generate256BitsOfRandomEntropy()
        {
            VAR randomBytes =新的字节[32]; // 32字节会给我们256位。
            使用(VAR rngCsp =新RNGCryptoServiceProvider())
            {
                //装满加密安全随机字节数组。
                rngCsp.GetBytes(randomBytes);
            }
            返回randomBytes;
        }
    }
}

以上类可以用类似下面的code很简单使用

 使用系统;命名空间EncryptStringSample
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            Console.WriteLine(请输入密码才能使用:);
            字符串密码=到Console.ReadLine();
            Console.WriteLine(请输入一个字符串加密:);
            字符串明文=到Console.ReadLine();
            Console.WriteLine();            Console.WriteLine(你的加密字符串是:);
            字符串encryptedstring = StringCipher.Encrypt(明文密码);
            Console.WriteLine(encryptedstring);
            Console.WriteLine();            Console.WriteLine(您解密的字符串是:);
            字符串decryptedstring = StringCipher.Decrypt(encryptedstring,密码);
            Console.WriteLine(decryptedstring);
            Console.WriteLine();            Console.WriteLine(preSS任意键退出...);
            到Console.ReadLine();
        }
    }
}

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

更新23 / DEC / 2015年:
到code具体的改进列表如下:


  • 修正了一个愚蠢的错误,其中编码是加密和之间是不同的
    解密。正如其盐放的机制;产生的IV值已经改变,编码不再是必要的。

  • 由于盐/ IV的变化,previous code注释错误地指出,UTF8编码16个字符的字符串产生的32个字节不再适用。
  • 的取代PBKDF1算法的使用已经被替换为更现代的PBKDF2算法的使用。

  • 密码导出现在可以正确咸而previously它不是在所有的腌制(另一种愚蠢的错误压扁)。

What is the most modern (best) way of satisfying the following in 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).

解决方案

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.

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 RijndaelManaged cryptography class.

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

The RijndaelManaged class 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.

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.

The "strength" of using this comes from using the RijndaelManaged class to perform the encryption for you, along with using the Rfc2898DeriveBytes function of the System.Security.Cryptography namespace 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).

Finally, it's important to note that this is still unauthenticated encryption. 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 post for 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).

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

  • 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).

这篇关于加密&安培;解密在C#中的String的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆