基於雜湊演算法的訊息認證碼(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後便無法通過認證了。