对称加密和解密
相信通过前面几页的叙述,大家已经明白了加密解密、数字签名的基本原理,下面我们看一下在.NET中是如何来支持加密解密的。正如上面我们所进行的分类,.NET中也提供了两组类用于加密解密,一组为对称加密,一组为非对称加密,如下图所示:
上面的类按照名称还可以分为两组,一组后缀为“CryptoServiceProvider”的,是对于底层Windows API的包装类,一组后缀为“Managed”,是在.NET中全新编写的类。现在假设我们以TripleDES作为算法,那么加密的流程如下:
- 先创建一个TripleDESCryptoServiceProvider的实例,实例名比如叫provider。
- 在 provider上指定密钥和IV,也就是它的Key属性和IV属性。这里简单解释一下IV(initialization vector),如果一个字符串(或者数据)加密之前很多部分是重复的比如ABCABCABC,那么加密之后尽管字符串是乱码,但相关部分也是重复的。为 了解决这个问题,就引入了IV,当使用它以后,加密之后即使是重复的也被打乱了。对于特定算法,密钥和IV的值可以随意指定,但长度是固定,通常密钥为 128位或196位,IV为64位。密钥和IV都是byte[]类型,因此,如果使用Encoding类来将字符串转换为byte[],那么编码方式就很 重要,因为UTF8是变长编码,所以对于中文和英文,需要特别注意byte[]的长度问题。
- 如 果是加密,在provider上调用CreateEncryptor()方法,创建一个ICryptoTransform类型的加密器对象;如果是解密, 在provider上调用CreateDecryptor()方法,同样是创建一个ICryptoTransform类型的解密器对象。 ICryptoTransform定义了加密转换的运算,.NET将在底层调用这个接口。
- 因 为流和byte[]是数据类型无关的一种数据结构,可以保存和传输任何形式的数据,区别只是byte[]是一个静态的概念而流是一个动态的概念。因 此,.NET采用了流的方式进行加密和解密,我们可以想到有两个流,一个是明文流,含有加密前的数据;一个是密文流,含有加密后的数据。那么就必然有一个中介者,将明文流转换为密文流;或者将密文流转换为明文流。.NET中执行这个操作的中介者也是一个流类型,叫做CryptoStream。它的构造函数如下,共有三个参数:
public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
- 当加密时,stream 为密文流(注意此时密文流还没有包含数据,仅仅是一个空流);ICryptoTransform是第3步创建的加密器,包含着加密的算 法;CryptoStreamMode枚举为Write,意思是将流经CryptoStream的明文流写入到密文流中。最后,从密文流中获得加密后的数 据。
- 当解密 时,stream为密文流(此时密文流含有数据);ICryptoTransform是第3步创建的解密器,包含着解密的算 法;CryptoStreamMode枚举为Read,意思是将密文流中的数据读出到byte[]数组中,进而再由byte[]转换为明文流、明文字符 串。
可见,CryptoStream总是接受密文流,并且根据CryptoStreamMode枚举的值来决定是将明文流写入到密文流(加密),还是将密文流读入到明文流中(解密)。下面是我编写的一个加密解密的Helper类:
public class CryptoHelper
{
// 对称加密算法提供器
private ICryptoTransform encryptor; // 加密器对象
private ICryptoTransform decryptor; // 解密器对象
private const int BufferSize = 1024;
public CryptoHelper(string algorithmName, string key)
{
SymmetricAlgorithm provider = SymmetricAlgorithm.Create(algorithmName);
provider.Key = Encoding.UTF8.GetBytes(key);
provider.IV = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
encryptor = provider.CreateEncryptor();
decryptor = provider.CreateDecryptor();
}
public CryptoHelper(string key) : this("TripleDES", key) { }
// 加密算法
public string Encrypt(string clearText)
{
// 创建明文流
byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText);
MemoryStream clearStream = new MemoryStream(clearBuffer);
// 创建空的密文流
MemoryStream encryptedStream = new MemoryStream();
CryptoStream cryptoStream =
new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write);
// 将明文流写入到buffer中
// 将buffer中的数据写入到cryptoStream中
int bytesRead = 0;
byte[] buffer = new byte[BufferSize];
do
{
bytesRead = clearStream.Read(buffer, 0, BufferSize);
cryptoStream.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
cryptoStream.FlushFinalBlock();
// 获取加密后的文本
buffer = encryptedStream.ToArray();
string encryptedText = Convert.ToBase64String(buffer);
return encryptedText;
}
// 解密算法
public string Decrypt(string encryptedText)
{
byte[] encryptedBuffer = Convert.FromBase64String(encryptedText);
Stream encryptedStream = new MemoryStream(encryptedBuffer);
MemoryStream clearStream = new MemoryStream();
CryptoStream cryptoStream =
new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
int bytesRead = 0;
byte[] buffer = new byte[BufferSize];
do
{
bytesRead = cryptoStream.Read(buffer, 0, BufferSize);
clearStream.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
buffer = clearStream.GetBuffer();
string clearText =
Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length);
return clearText;
}
public static string Encrypt(string clearText, string key)
{
CryptoHelper helper = new CryptoHelper(key);
return helper.Encrypt(clearText);
}
public static string Decrypt(string encryptedText, string key)
{
CryptoHelper helper = new CryptoHelper(key);
return helper.Decrypt(encryptedText);
}
}
string key = "ABCDEFGHIJKLMNOP";
string clearText = "hello world";
CryptoHelper helper = new CryptoHelper(key);
string encryptedText = helper.Encrypt(clearText);
Console.WriteLine(encryptedText);
clearText = CryptoHelper.Decrypt(encryptedText, key);
Console.WriteLine(clearText);
Console.ReadKey(true);