对称加密
(本文系张子阳《.net之美》读书笔记,文中多处引用书本内容)
流
首先简单介绍一下流的概念。
流的最主要用途就是与应用程序外部的文件或数据源进行数据交互。考虑将一个TXT文件从D盘拷贝到C盘。
它的拷贝过程应该是这样的:
源文件在磁盘中,需要建立一个类似管道的东西将文件和内存中的应用程序连接起来,并且将文件按字节发送。创建一个byte[]数组用于存放字节。
接下来,在C盘创建目标文件,同样通过一个管道一样的东西,将字节写入管道中,间接地写到文件中去。
而流,就是这种管道,或者说起到管道的作用。
下面简单学习一下代码:
1 Stream source = new FileStream("D:\\test.txt", FileMode.Open, FileAccess.Read);//读取“管道” 2 int size = 10; 3 byte[] buffer = new byte[size];//用于储存临时字节 4 int bytesRead; 5 Stream target = new FileStream("C:\\test.txt", FileMode.Create, FileAccess.Write);//写入“管道” 6 //因不知源文件字节长度,只能通过循环,每次读取一定数量的字节并写入流 7 do 8 { 9 bytesRead = source.Read(buffer, 0, size); 10 target.Write(buffer, 0, bytesRead);//第三个参数是从Buffer中读取的个数,应该是bytesRead不是size 11 } while (bytesRead > 0); 12 13 source.Dispose(); 14 target.Dispose();
或者使用流包装器类(不是流),StreamReader和StreamWriter,用法与上面差不多,注意已不是字节流,而是字符流
1 StreamReader sr = new StreamReader("D:\\test.txt"); 2 StreamWriter sw = new StreamWriter("C:\\test2.txt"); 3 int maxSize = 10; 4 char[] charBuffer = new char[maxSize];//注意,字符流 5 int charRead; 6 do 7 { 8 charRead = sr.Read(charBuffer, 0, maxSize); 9 sw.Write(charBuffer, 0, charRead); 10 } while (charRead > 0); 11 12 sr.Dispose(); 13 sw.Dispose();
流还包括装饰器流,包装器类还包括BinaryReader和BinaryWriter。在这里就不详细展开了。
对称加密
加密和解密属于数据安全的范畴,在消息传输时,通过对消息进行特殊编码(即加密),建立一种安全的交流方式,使得只有发送者所期望的接收者能够理解(即解密)。一般场景中有三种角色:发送方,接收方,第三方。
消息加密应同时满足以下三个条件:
1)完整性,消息的可以确定消息在传输途中没有被篡改过,消息是完好无损的。
2)保密性,消息的发送方能够确定消息只有预期的接收方可以理解或解密(不保证第三方无法获得,但保证第三方无法解密)。
3)可认证性,消息的接收方可以确定消息是由谁发送的(接收方可以确认消息确定是由预期的发送方发送的)。
消息加密有散列运算(即Hash运算)、对称加密、非对称加密等,这里介绍一下对称加密(比较简单易于理解)。
对称加密流程如下:
1)发送方和接收方持有相同的密钥,并严格保密。
2)发送方使用密钥对消息进行加密,然后发送消息。
3)接收方收到消息后,使用同样的密钥对消息进行解密。
在上述流程中,第三方可能截取消息,但得到一堆乱码,没有密钥解密。
.net提供了一组类型(命名空间System.Security.Cryptography)来实现对称加密和解密。这些类型拥有共同的基类SymmetricAlgorithm,见下图。
可以看到上面类型分为CryptoServiceProvider和Managed两类,前者是由托管代码写的,后者调用的是Windows Crypto API,相当于一个包装类。
TripleDES算法加密
假设选择TripleDES算法进行加密,步骤如下:
1)创建一个TripleDESCryptoServiceProvider实例(假定变量名为provider)
2)在provider上指定密钥和IV。密钥通常为128位或192位字节,IV为64位。IV是为了解决同样的字符加密后字符仍一样的问题,例如ABABABCDE加密后,前面三个AB的数据理论上是一样且位置不变,引入IV之后即使是重复的也被打乱了。
3)利用provider创建加密器或解密器。ICryptoTransform定义了加密转换的运算,.net在底层调用这个接口。加密器:CreateEncryptor()方法,解密器:CreateDecryptor()方法。
4).net采用流的方式进行加密和解密,运算过程会涉及两个流,一个是明文流,包含加密前的数据,一个是密文流,包含加密后的数据。在两者之间,有一个中介者,负责将明文流转换为密文流,以及将密文流转换为明文流。这个中介者也是一个流类型,叫CryptoStream,它的构造函数有三个参数
public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode),其中每次传入的流都是密文流,根据需要传入加密器或解密器,以及写或读模式。
5)创建完以上几个对象,就可以进行转换了,加密过程为:明文流读字节,CryptoStream写密文流;解密过程为:CryptoStream读密文流,明文流写字节。
密钥应注意一点:3DES算法,如果1重、2重和3重密钥(每8位)有一个相同,算法会退化为2重或1重,运行报错。见下图:
加解密过程可以简单归纳如下:
1)创建TripleDESCryptoServiceProvider对象,指定密钥和IV,创建加密器或解密器
2)创建明文流和密文流
3)创建转换中介者CryptoStream对象,将密文流传入。
4)加密:明文流读字节,CryptoStream写密文流;解密:CryptoStream读密文流,明文流写字节。
实例
加密帮助类
1 using System; 2 using System.Security.Cryptography; 3 using System.IO; 4 using System.Text; 5 6 namespace ch09 7 { 8 class EncryptorHelper 9 { 10 //密钥应注意一点:3DES算法,如果1重、2重和3重密钥(每8位)有一个相同,算法会退化为2重或1重,运行报错 11 private static string _rgbKey = "thisishelloworld"; 12 private static byte[] _rgbIV = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; 13 14 public static string Encryptor(string plainText) 15 { 16 TripleDESCryptoServiceProvider provider = new TripleDESCryptoServiceProvider();//创建provider,利用其产生加密器 17 provider.Key = Encoding.Default.GetBytes(_rgbKey); 18 provider.IV = _rgbIV; 19 ICryptoTransform encryptor = provider.CreateEncryptor();//加密器 20 byte[] plainBytes = Encoding.GetEncoding("gb2312").GetBytes(plainText); 21 MemoryStream plainStream = new MemoryStream(plainBytes);//创建明文流 22 MemoryStream cipherStream = new MemoryStream();//创建密文流 23 CryptoStream enCryptoStream = new CryptoStream(cipherStream, encryptor, CryptoStreamMode.Write);//创建加密中介 24 int maxSize = 10; 25 byte[] buffer = new byte[maxSize]; 26 int bytesRead = 0; 27 do 28 { 29 bytesRead = plainStream.Read(buffer, 0, maxSize); 30 enCryptoStream.Write(buffer, 0, bytesRead);//谨记这里应该是写入读出的长度,而不是buffer本身的长度 31 } while (bytesRead > 0); 32 enCryptoStream.FlushFinalBlock(); 33 return Convert.ToBase64String(cipherStream.ToArray()); 34 35 } 36 37 38 public static string Decryptor(string cipherText) 39 { 40 TripleDESCryptoServiceProvider provider = new TripleDESCryptoServiceProvider(); 41 provider.Key = Encoding.Default.GetBytes(_rgbKey); 42 provider.IV = _rgbIV; 43 ICryptoTransform decryptor = provider.CreateDecryptor(); 44 byte[] cipherByte = Convert.FromBase64String(cipherText); 45 MemoryStream plainStream = new MemoryStream(); 46 MemoryStream cipherStream = new MemoryStream(cipherByte); 47 CryptoStream deCryptoStream = new CryptoStream(cipherStream, decryptor, CryptoStreamMode.Read); 48 int maxSize = 10; 49 byte[] buffer = new byte[maxSize]; 50 int bytesRead = 0; 51 do 52 { 53 bytesRead = deCryptoStream.Read(buffer, 0, maxSize); 54 plainStream.Write(buffer, 0, bytesRead); 55 } while (bytesRead > 0); 56 57 deCryptoStream.Dispose(); 58 return Encoding.GetEncoding("gb2312").GetString(plainStream.ToArray()); 59 } 60 } 61 }
使用帮助类
1 string plainText = "我要握住一个最美的梦,给未来的自己~~real me!"; 2 Console.WriteLine("原文:" + plainText); 3 string encryptedString = EncryptorHelper.Encryptor(plainText); 4 Console.WriteLine("加密后:" + encryptedString); 5 Console.WriteLine("解密:" + EncryptorHelper.Decryptor(encryptedString)); 6 Console.Read();
输出