一、c#中的对称加密概述
对称加密算法在加密和解密时使用相同的密钥。Framework提供了四种对称加密算法。AES、DES、Rijndael、RC2。
DES:全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
RC2:RC2是由著名密码学家Ron Rivest设计的一种传统对称分组加密算法,它可作为DES算法的建议替代算法。
Rijndael:这个类从.NET Framework 1.0开始就已经存在了。
Aes:这个类是在.NET Framework 3.5中引入的。
这些算法中Rijndael算法是最优秀的。Rijndael即快速又安全。它拥有两个实现:
Rijndael和Aes这两个实现几乎是等价的,但是Aes不允许通过更改块尺寸来削弱加密强度。CLR安全团队推荐使用Aes类。
Rijndael和Aes支持16字节、24字节和32字节的对称密钥长度:这几种长度目前均认为是安全的。
关于AES加密算法的细节,可参考下面这篇文章。
AES加密算法的详细介绍与实现_TimeShatter的博客-CSDN博客_aes加密AES简介高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图: 下面简单介绍下各个部分的作用与意义:明文P 没有经过加密的数据。密钥K 用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生https://blog.csdn.net/qq_28205153/article/details/55798628
二、加解密示例
//加密
byte[] key = { 99, 66, 33, 11, 8, 6,59,68,21,25,36,21,24,6,6,6 };
byte[] iv = { 99, 66, 33, 11,5,5,5,5,5,6,6,6,5,5,5,9 };
byte[] data = { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
using (SymmetricAlgorithm symmetric = Aes.Create())
{
using (ICryptoTransform tran = symmetric.CreateEncryptor(key, iv))
{
using (Stream f = File.Create("aes.txt"))
{
using (Stream c = new CryptoStream(f, tran, CryptoStreamMode.Write))
{
c.Write(data, 0, data.Length);
}
}
}
}
//解密
using (SymmetricAlgorithm symmetric = Aes.Create())
{
using (ICryptoTransform tran = symmetric.CreateDecryptor(key, iv))
{
using (Stream f = File.OpenRead("aes.txt"))
{
using (Stream c = new CryptoStream(f, tran, CryptoStreamMode.Read))
{
for(int b; (b=c.ReadByte())>-1;)
{
Console.WriteLine(b + " ");
}
}
}
}
}
如果使用错误的密钥进行解密,则CryptoStream会抛出CryptographicException,而捕获该异常是测试密钥是否正确的唯一途径。
除了密钥之外,示例中还生成了一个初始化向量(Initialization Vector, IV)。这16字节的序列也是密码的一部分(和密钥相似),但它并不是保密的。当传输加密信息时,IV会以明文的方式进行传输(可能是在消息的头部进行传输),但每一段信息中其值都不相同。这样即使有些消息的未加密版本都是类似的,但是加密后的信息也会难以识别。
如果无须IV的保护,可令16字节密钥和IV的值相同。但是,使用相同的IV发送多条消息会削弱密码的安全性并使破解的可能性大大增加。
CryptoStream则像是管道工,它关注于流的处理。因此我们可以将Aes替换为另一种对称加密算法而仍旧使用CryptoStream。
CryptoStream是双向的。因此可以使用CryptoStreamMode.Read来读取流,也可以使用CryptoStreamMode.Write来写入流。加密器和解密器,与读取和写入可以形成四种组合。这些组合可能会令人茫然!为了帮助理解,可以将读取理解为“拉”,而将写入理解为“推”。如果仍旧有疑问则可以首先用Write操作进行加密,而用Read操作进行解密,这种组合是最自然也最常见的。
请使用System.Cryptography命名空间下的RandomNumberGenerator来生成随机密钥或IV。此类随机数生成器生成的数字是真正难以预测的,或称为具备密码学强度的(而System.Random则无法保证这一点)。以下是一个示例:
//随机生成密钥
byte[] key_random = new byte[16];
byte[] iv_random = new byte[16];
RandomNumberGenerator rand = RandomNumberGenerator.Create();
//随机生成一个key
rand.GetBytes(key_random);
//随机生成一个iv
rand.GetBytes(iv_random);
如果不指定密钥和IV,则加密算法会自动生成密码学强度的随机数作为密钥和IV。这些值可以通过Aes对象的Key和IV属性进行查看。
三、内存加密
我们可以使用MemoryStream完全将加密和解密放在内存中。
public class AesTools
{
static byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
using (SymmetricAlgorithm symmetric = Aes.Create())
{
symmetric.KeySize = 128;
symmetric.BlockSize = 128;
symmetric.Mode = CipherMode.CBC;
symmetric.Padding = PaddingMode.PKCS7;
using (ICryptoTransform tran = symmetric.CreateEncryptor(key, iv))
{
return EnCrypt(data, tran);
}
}
}
static byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
{
using (SymmetricAlgorithm symmetric = Aes.Create())
{
symmetric.KeySize = 128;
symmetric.BlockSize = 128;
symmetric.Mode = CipherMode.CBC;
symmetric.Padding = PaddingMode.PKCS7;
using (ICryptoTransform tran = symmetric.CreateDecryptor(key, iv))
{
return DeCrypt(data, tran);
}
}
}
static byte[] EnCrypt(byte[] data, ICryptoTransform tran)
{
return tran.TransformFinalBlock(data, 0, data.Length);
}
static byte[] DeCrypt(byte[] data, ICryptoTransform tran)
{
return tran.TransformFinalBlock(data, 0, data.Length);
}
public static string Encrypt(string data, byte[] key, byte[] iv)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data), key, iv));
}
public static string Decrypt(string data, byte[] key, byte[] iv)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data), key, iv));
}
}
测试代码
//随机生成密钥
byte[] key_random = new byte[16];
byte[] iv_random = new byte[16];
RandomNumberGenerator rand = RandomNumberGenerator.Create();
//随机生成一个key
rand.GetBytes(key_random);
//随机生成一个iv
rand.GetBytes(iv_random);
string encrypted = AesTools.Encrypt("yeah小小的猫咪", key_random, iv_random);
Console.WriteLine(encrypted);
string decrypted = AesTools.Decrypt(encrypted, key_random, iv_random);
Console.WriteLine(decrypted);
可以看到如下输出
四、串联加密流
CryptoStream是一个装饰器,它可以将其他的流串联起来。以下示例会先将压缩的加密文本写入文件,之后再从该文件中读取这些内容。
所有名称为单个字符的变量都是链条的一部分。而那些algorithm、encryptor和decryptor将协助CryptoStream进行加密工作。
不论流的大小如何,上述串联加密流的方式只会占用很少的内存。