基于哈希算法的消息认证码(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对数据进行签名,我们把根据密钥计算好的哈希值保存在数据内容的前面,最后返回整个字节数组:
//数据签名
staticbyte[] SignData(byte[] key, byte[] data, HMAC alg)
{
//设置密钥
alg.Key = key;
//计算哈希值
var hash = alg.ComputeHash(data);
//返回具有签名的数据(哈希值+数组本身)
return hash.Concat(data).ToArray();
}
数据认证则是先提取收到的哈希值和数据内容,最后再利用密钥对收到的数据内容进行哈希值的计算,拿这个计算好的哈希值和收到的哈希值作比较,如果相同则数据完整,不同则数据已经被修改。
//数据认证
staticbool 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);
}
而如果一个被签名的数据被修改了,那么根据双方都知道的密钥就可以判断出数据不完整(已被修改)。
完整代码:
staticvoid Main()
{
//使用SHA1的HMAC
HMAC hmac =HMACSHA1.Create();
//源数据
var data =newbyte[] { 1, 2, 3, 4, 5, 6, 7 };
//密钥
var key =newbyte[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) ?"数据正确" : "数据已被修改");
}
//数据签名
staticbyte[] SignData(byte[] key, byte[] data, HMAC alg)
{
//设置密钥
alg.Key = key;
//计算哈希值
var hash = alg.ComputeHash(data);
//返回具有签名的数据(哈希值+数组本身)
return hash.Concat(data).ToArray();
}
//数据认证
staticbool 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);
}
staticvoid 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后便无法通过认证了。