基于哈希算法的消息认证码(HMAC:Hash-based Message Authentication Code )在.NET也可以很轻松地实现。
在目前的.NET Framework中(.NET 4.0)HMAC位于如下位置:
HMAC继承与HashAlgorithm,后者代表抽象的哈希(散列)算法,而他的直接父类:KeyedHashAlgorithm代表有密钥的哈希算法。KeyHashAlgorithm定义了新的属性成员Key代表密钥。而HMAC类还定义了BlockSizeValue代表HMAC处理块的大小(单位:比特)。
这就是HMAC相比.NET中的常规哈希算法(HashAlgorithm类)的区别,并没有加入太多复杂的东西。
最后注意HMAC是基于一个哈希算法的,HMAC的安全程度也跟这个背后的哈希算法紧紧相关,你可以选择MD5, SHA1, SHA256, SHA512……等许多哈希算法。
着我们利用HMAC做一个简单的数据验证方法。
首先利用HMAC对数据进行签名,我们把根据密钥计算好的哈希值保存在数据内容的前面,最后返回整个字节数组:
//数据签名 static byte[] SignData(byte[] key, byte[] data, HMAC alg) { //设置密钥 alg.Key = key; //计算哈希值 var hash = alg.ComputeHash(data); //返回具有签名的数据(哈希值+数组本身) return hash.Concat(data).ToArray(); }
数据认证则是先提取收到的哈希值和数据内容,最后再利用密钥对收到的数据内容进行哈希值的计算,拿这个计算好的哈希值和收到的哈希值作比较,如果相同则数据完整,不同则数据已经被修改。
//数据认证 static bool VerityData(byte[] key, byte[] data, HMAC alg) { //提取收到的哈希值 var receivedHash = data.Take(alg.HashSize >> 3); //提取数据本身 var dataContent = data.Skip(alg.HashSize >> 3).ToArray(); //设置密钥 alg.Key = key; //计算数据哈希值和收到的哈希值 var computedHash = alg.ComputeHash(dataContent); //如果相等则数据正确 return receivedHash.SequenceEqual(computedHash); }
而如果一个被签名的数据被修改了,那么根据双方都知道的密钥就可以判断出数据不完整(已被修改)。
完整代码:
static void Main() { //使用SHA1的HMAC HMAC hmac = HMACSHA1.Create(); //源数据 var data = new byte[] { 1, 2, 3, 4, 5, 6, 7 }; //密钥 var key = new byte[100]; //创建一个随即密钥 using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(key); } //对数据进行签名 var signedData = SignData(key, data, hmac); //输出数据 PrintData(signedData, hmac); //认证 Console.WriteLine(VerityData(key, signedData, hmac) ? "数据正确" : "数据已被修改"); //故意修改数据(将源数据的5修改成4) signedData[(hmac.HashSize >> 3) + 4] = 4; //输出数据 PrintData(signedData, hmac); //认证 Console.WriteLine(VerityData(key, signedData, hmac) ? "数据正确" : "数据已被修改"); } //数据签名 static byte[] SignData(byte[] key, byte[] data, HMAC alg) { //设置密钥 alg.Key = key; //计算哈希值 var hash = alg.ComputeHash(data); //返回具有签名的数据(哈希值+数组本身) return hash.Concat(data).ToArray(); } //数据认证 static bool VerityData(byte[] key, byte[] data, HMAC alg) { //提取收到的哈希值 var receivedHash = data.Take(alg.HashSize >> 3); //提取数据本身 var dataContent = data.Skip(alg.HashSize >> 3).ToArray(); //设置密钥 alg.Key = key; //计算数据哈希值和收到的哈希值 var computedHash = alg.ComputeHash(dataContent); //如果相等则数据正确 return receivedHash.SequenceEqual(computedHash); } static void PrintData(byte[] data, HMAC alg) { Console.WriteLine("哈希值: {0}\n文件值: {1}", BitConverter.ToString(data.Take(alg.HashSize >> 3).ToArray()), BitConverter.ToString(data.Skip(alg.HashSize >> 3).ToArray())); }
输出:
哈希值: 39-FD-E8-2D-3C-0D-5F-A5-53-7A-37-3C-9B-F6-17-D9-CC-0D-FC-90 文件值: 01-02-03-04-05-06-07 数据正确 哈希值: 39-FD-E8-2D-3C-0D-5F-A5-53-7A-37-3C-9B-F6-17-D9-CC-0D-FC-90 文件值: 01-02-03-04-04-06-07 数据已被修改
当源数据的5被改成4后便无法通过认证了。