在C#中encryption和解密一个string

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

string encryptedString = SomeStaticClass.Encrypt(sourceString); string decryptedString = SomeStaticClass.Decrypt(encryptedString); 

但是最小的问题涉及到盐,密钥,用byte []等操作。

一直在谷歌search和困惑,我发现(你可以看到类似的SO问清单,看看这是一个欺骗性的问题)。

更新23 / Dec / 2015:由于这个答案似乎得到了很多upvotes,我已经更新它来修复愚蠢的错误,并通常基于意见和反馈改进代码。 查看post末尾的特定改进列表。

正如其他人所说,密码学并不简单,所以最好避免“滚动自己的”encryptionalgorithm。

但是,您可以围绕类似于内置的RijndaelManagedencryption类的“包装自己的”包装类。

Rijndael是当前高级encryption标准的algorithm名称,所以您当然正在使用一种可被认为是“最佳实践”的algorithm。

RijndaelManaged类确实通常要求你用字节数组,salt,键,初始化向量等来“解决”,但这恰恰是在你的“包装”类中可以被抽象出来的那种细节。

下面这个类是我前一段时间写的,用来完成你之后的事情,一个简单的单一的方法调用,允许一些基于string的明文用基于string的密码进行encryption,最终的encryptionstring也是被表示为一个string。 当然,还有一个相同的方法来使用相同的密码来解密encryption的string。

与这个代码的第一个版本不同,它每次都使用完全相同的salt和IV值,这个更新的版本每次都会产生随机的salt和IV值。 由于salt和IV在给定string的encryption和解密之间必须是相同的,所以salt和IV在encryption时被预置于密文并且从中再次提取以便执行解密。 这样做的结果是每次使用完全相同的密码encryption完全相同的明文给出与完全不同的密文结果。

使用此function的“优点”来自于使用RijndaelManaged类为您执行encryption,同时使用System.Security.Cryptography命名空间的Rfc2898DeriveBytes函数,该函数将使用标准和安全的algorithm(特别是PBKDF2 )基于您提供的基于string的密码。 (请注意,这是第一版使用旧版PBKDF1algorithm的改进)。

最后,请注意,这仍然是未经validation的encryption。 单独的encryption仅提供隐私(即,第三方不知道消息),而经authentication的encryption旨在提供隐私和真实性(即,接收者知道消息是由发送者发送的)。

不知道你的具体要求,很难说这里的代码是否足够满足你的需求,但是,它是为了在实现相对简单和“质量”之间取得很好的平衡。 例如,如果encryptionstring的“接收者”直接从受信任的“发件人”接收string,则authentication可能甚至不是必需的 。

如果你需要更复杂的东西,并提供authentication的encryption,请查看这个post的实现。

代码如下:

 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样品解决scheme(包括几个unit testing)。

更新23 / Dec / 2015:代码的具体改进列表如下:

  • 修正了在encryption和解密之间编码不同的愚蠢错误。 由于盐和IV值生成的机制已经改变,编码不再是必要的。
  • 由于salt / IV的变化,前面的代码注释错误地表明编码16个字符的string的UTF8产生32个字节不再适用(因为不再需要编码)。
  • 被替代的PBKDF1algorithm的用法已经被更现代的PBKDF2algorithm所取代。
  • 密码派生现在是正确的盐渍,而以前它根本没有盐渍(另一个愚蠢的错误挤压)。
 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); 

如果你需要在内存中存储密码,并希望encryption,你应该使用SecureString

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

对于更一般的用途,我会使用FIPS认可的algorithm,如先前称为Rijndael的高级encryption标准(Advanced Encryption Standard)。 看到这个页面的一个实现的例子:

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

您可能正在寻找ProtectedData类,它使用用户的login凭据encryption数据。

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

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

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

然后,您将能够注入IDataProtectionProvider实例并使用它来encryption/解密数据:

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

看到这篇文章的更多细节。

我见过的最简单的encryption方式是通过RSA

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

它确实涉及到使用字节,但是当它涉及到它时,你需要encryption和解密来弄清楚,否则这将很容易被破解。