net加密基础3-数据完整性(散列)
数据完整性概念为什么需要数据完整性?
之前的数据加密技术可以保护信息不被第三方获取,但是不能阻止恶意用户对信息的篡改。
假如:用户A发送一个加密信息给解密用户B,很长的时间都是正常运行,但是突然有一天用户A的电脑被用户C获取了,但是用户C不知道密钥,所以无法和用户B进行沟通,但是用户C可以修改包含恶意数据的信息发给用户B。
用户B无法解密这些信息,这些恶意的信息还可能对用户B的电脑进行攻击。
所以验证数据的完整性和正确性就很重要了,特别是一些涉及到金融和对安全性要求高的项目,对数据的完整性和正确性要求就更加严格了!
散列算法
什么是散列算法?
散列算法可以使用任意数量的数据,并且用它生成该信息的唯一较小的散列码。
散列算法的特点
1.单向
如果你想要根据散列码来还原文档是完全不可能的(除非你具有逆天的能力!),因为散列不包含文档的所有信息。
2.最小的冲突
几乎是不可能创建出2个散列码一样的文档(如果你无意成功了,请去把你所有家当拿去买易做图吧,记得支援我点!)。
以上这些原因使散列成为验证数据完整性的最佳算法。
NET中的散列类 |
NET中所有的散列算法都继承HashAlgorithm抽象类。
算法 | 抽象散列算法类 | 实现类 | 散列长度(位) |
MD5 | MD5 | MD5CryptoServiceProvider | 128 |
SHA-1 | SHA1 | SHA1CryptoServiceProvider | 160 |
SHA-256 | SHA256 | SHA256Managed | 256 |
SHA-384 | SHA384 | SHA384Managed | 384 |
SHA512 | SHA512 | SHA512Managed | 512 |
散列长度和安全性
对于大部分散列算法来说,散列的长度越长,散列码抵抗蛮力攻击的能力就越强,发现相同散列文档也就越难。
如果一个散列有256位,那么它就有2^256次方这么多种不同的散列码,貌似宇宙也只有10^66种原子!现在知道你是在逆天了吧!
下面我们使用MD5实现个简单的数据散列
static void Main(string[] args) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); Console.WriteLine("MD5 Hash size:{0} bits", md5.HashSize); Console.WriteLine("MD5 Hash size:{0} bytes", md5.HashSize / 8); Console.WriteLine(); byte[] data = { 0x13, 0x45, 0x5A, 0xB6 }; //计算data的散列码 byte[] HashBytes = md5.ComputeHash(data); Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length); Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes)); Console.Read(); }
多次运行这个程序,如果不修改byte数组的数据,那么生成的散列码都是相同的。
对于不同的MD5对象去计算相同的数据散列,其结果是相同的。
static void Main(string[] args) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); MD5CryptoServiceProvider md51 = new MD5CryptoServiceProvider(); byte[] data = { 0x13, 0x45, 0x5A, 0xB6 }; //计算data的散列码 byte[] HashBytes = md5.ComputeHash(data); Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length); Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes)); byte[] HashBytes1 = md5.ComputeHash(data); Console.WriteLine("HashBytes1 size:{0} bytes", HashBytes1.Length); Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes1)); Console.Read(); }
散列对数据流的支持
ComputeHash有个重载的方法,接受一个流对象,然后从流的当前位置读取完整的流数据并且对其计算散列。
View Code
static void Main(string[] args) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); byte[] data = { 0x13, 0x45, 0x5A, 0xB6 }; MemoryStream ms = new MemoryStream(); ms.Write(data, 0, data.Length); //计算data的散列码 byte[] HashBytes = md5.ComputeHash(ms); Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length); Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes)); Console.Read(); }
请注意散列以后的流的内部指针位置,如果需要重新使用该流,那么请重新设置该流的位置。
散列CryptoStream中的数据
回忆之前我们对称加密算法中对CryptoStream的使用,
DESdes = DESCryptoServiceProvider.Create();
MemoryStreamms = newMemoryStream();
CryptoStreamcs = newCryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
这里des.CreateEncryptor()方法返回的是ICryptoTransform,我们再来看看MD5的基类实现。
publicabstractclassMD5: HashAlgorithm
publicabstractclassHashAlgorithm: ICryptoTransform, IDisposable
所以我们可以把散列对象传入CryptoStream中(注意CryptoStream必须是在写的模式下)。
View Code
static void Main(string[] args) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); byte[] data = { 0x13, 0x45, 0x5A, 0xB6 }; MemoryStream ms = new MemoryStream(); ms.Write(data, 0, data.Length); MemoryStream ms1 = new MemoryStream(); CryptoStream cry = new CryptoStream(ms1, md5, CryptoStreamMode.Write); cry.Write(data, 0, data.Length); cry.FlushFinalBlock(); ms1.Close(); byte[] HashBytes = md5.Hash; Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length); Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes)); Console.Read(); }
介绍完了散列的基本用法,下面介绍下散列的应用
加密散列码
使用散列就是为了验证数据的完整性,那么如果非法用户向服务器发送了一条错误的信息,并且对其生成了正确的散列码,那么服务器验证发现散列码是正确的,但是却无法对数据正确的解密,而且那些数据很有可能是包含了恶意的攻击代码。
所以在实际的应用中一般是把明文信息和散列码一起加密发送到解密方,但是这
补充:综合编程 , 安全编程 ,