如何在c#中加密和解密字符串?
当前回答
BouncyCastle是一个很棒的。net加密库,它可以作为Nuget包安装到你的项目中。比起目前System.Security.Cryptography库中可用的东西,我更喜欢它。它为你提供了更多可用算法的选择,并为这些算法提供了更多的模式。
这是一个TwoFish实现的例子,它是由Bruce Schneier(我们所有偏执的人的英雄)编写的。这是一个像Rijndael一样的对称算法 (又名AES)。它是AES标准的三个最终入选者之一,是Bruce Schneier编写的另一个著名算法BlowFish的兄弟姐妹。
使用bouncycastle的第一件事是创建一个加密器类,这将使它更容易在库中实现其他块密码。下面的加密器类接受一个泛型参数T,其中T实现了IBlockCipher,并有一个默认构造函数。
UPDATE: Due to popular demand I have decided to implement generating a random IV as well as include an HMAC into this class. Although from a style perspective this goes against the SOLID principle of single responsibility, because of the nature of what this class does I reniged. This class will now take two generic parameters, one for the cipher and one for the digest. It automatically generates the IV using RNGCryptoServiceProvider to provide good RNG entropy, and allows you to use whatever digest algorithm you want from BouncyCastle to generate the 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;
}
}
接下来只需在新类上调用加密和解密方法,下面是使用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);
最后,如果你想使用AES和SHA256 HMAC,你可以做以下事情:
var aes = new Encryptor<AesEngine, Sha256Digest>(Encoding.UTF8, key, hmacKey);
cipher = aes.Encrypt("TEST");
plainText = aes.Decrypt(cipher);
The hardest part about encryption actually deals with the keys and not the algorithms. You'll have to think about where you store your keys, and if you have to, how you exchange them. These algorithms have all withstood the test of time, and are extremely hard to break. Someone who wants to steal information from you isn't going to spend eternity doing cryptanalysis on your messages, they're going to try to figure out what or where your key is. So #1 choose your keys wisely, #2 store them in a safe place, if you use a web.config and IIS then you can encrypt parts of the the web.config, and finally if you have to exchange keys make sure that your protocol for exchanging the key is secure.
更新2 改变比较方法以减轻定时攻击。点击这里查看更多信息http://codahale.com/a-lesson-in-timing-attacks/。还更新到默认PKCS7填充,并添加了新的构造函数,以允许最终用户选择他们想要使用的填充。感谢@CodesInChaos的建议。
其他回答
加密是编程中非常常见的问题。我认为最好是安装一个包来为您做这个任务。也许是一个简单的开源NuGet项目 简单Aes加密
密钥在配置文件中,因此很容易在生产环境中更改,而且我没有看到任何缺点。
<MessageEncryption>
<EncryptionKey KeySize="256" Key="3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8="/>
</MessageEncryption>
如果您正在使用ASP。你现在可以使用。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之外设置。
我还没找到非asp的。系统外的净等值。网络名称空间。
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();
}
}
}
}
}
}
}
用于AES-GCM加密的BouncyCastle的替代方案是libsodium-net。它包装了libsodium C库。一个很好的优点是它在cpu中使用AES-NI扩展进行非常快速的加密。缺点是,如果CPU没有扩展,它根本无法工作。软件没有退路。
下面是一个使用RSA的例子。
重要:使用RSA加密KeySize - MinimumPadding加密的数据大小是有限制的。例如256字节(假设2048位密钥)- 42字节(最小OEAP填充)= 214字节(最大明文大小)
用RSA密钥替换your_rsa_key。
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