信息摘要算法之五:HMAC算法分析與實現
MAC(Message Authentication Code,消息認證碼算法)是含有密鑰散列函數算法,兼容了MD和SHA算法的特性,並在此基礎上加上了密鑰。因此MAC算法也經常被稱作HMAC算法。
1、HMAC概述
HMAC算法首先它是基於信息摘要算法的。目前主要集合了MD和SHA兩大系列消息摘要算法。其中MD系列的算法有HmacMD2、HmacMD4、HmacMD5三種算法;SHA系列的算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512五種算法。
HMAC算法除了需要信息摘要算法外,還需要一個密鑰。HMAC的密鑰可以是任何長度,如果密鑰的長度超過了摘要算法信息分組的長度,則首先使用摘要算法計算密鑰的摘要作為新的密鑰。一般不建議使用太短的密鑰,因為密鑰的長度與安全強度是相關的。通常選取密鑰長度不小於所選用摘要算法輸出的信息摘要的長度。
2、HMAC算法分析
HMAC算法本身並不復雜,起需要有一個哈希函數,我們記為H。同時還需要有一個密鑰,我們記為K。每種信息摘要函數都對信息進行分組,每個信息塊的長度是固定的,我們記為B(如:SHA1為512位,即64字節)。每種信息摘要算法都會輸出一個固定長度的信息摘要,我們將信息摘要的長度記為L(如MD5為16字節,SHA-1為20個字節)。正如前面所述,K的長度理論上是任意的,一般為了安全強度考慮,選取不小於L的長度。
HMAC算法其實就是利用密鑰和明文進行兩輪哈希運算,以公式可以表示如下:
HMAC(K,M)=H(K⊕opad∣H(K⊕ipad∣M)),其中:
Ipad為0x36重復B次
Opad為0x5c重復B次
M 代表一個消息輸入
根據上面的算法表示公式,我們可以描述HMAC算法的運算步驟:
(1)、檢查密鑰K的長度。如果K的長度大於B則先使用摘要算法計算出一個長度為L的新密鑰。如果後K的長度小於B,則在其後面追加0來使其長度達到B。
(2)、將上一步生成的B字長的密鑰字符串與ipad做異或運算。
(3)、將需要處理的數據流text填充至第二步的結果字符串中。
(4)、使用哈希函數H計算上一步中生成的數據流的信息摘要值。
(5)、將第一步生成的B字長密鑰字符串與opad做異或運算。
(6)、再將第四步得到的結果填充到第五步的結果之後。
(7)、使用哈希函數H計算上一步中生成的數據流的信息摘要值,輸出結果就是最終的HMAC值。
由上述描述過程,我們知道HMAC算法的計算過程實際是對原文做了兩次類似於加鹽處理的哈希過程。
3、代碼實現
前面我們描述了HMAC算法及其實現過程,接下來我們則將其實現。首先我們定義一個用於保存計算過程上下文的結構:
/** 該結構將為HMAC密鑰哈希操作保存上下文信息*/
typedef struct HMACContext {
int whichSha; /* 所用的SHA算法 */
int hashSize; /* 所用SHA的哈希值大小 */
int blockSize; /* 所用SHA塊的大小 */
SHAContext shaContext; /* SHA上下文 */
unsigned char k_opad[SHA_Max_Message_Block_Size];/* outer padding - key XORd with opad */
int Computed; /* Is the MAC computed? */
int Corrupted; /* Cumulative corruption code */
} HMACContext;
接下來實現HMAC初始化函數。這個函數將初始化hmacContext以準備計算一個新的HMAC消息摘要。
int hmacReset(HMACContext *context, enum SHAversion whichSha,const unsigned char *key, int key_len)
{
int i, blocksize, hashsize, ret;
unsigned char k_ipad[SHA_Max_Message_Block_Size];/* inner padding - key XORd with ipad */
unsigned char tempkey[SHAMaxHashSize];/* temporary buffer when keylen > blocksize */
if (!context) return shaNull;
context->Computed = 0;
context->Corrupted = shaSuccess;
blocksize = context->blockSize = SHABlockSize(whichSha);
hashsize = context->hashSize = SHAHashSize(whichSha);
context->whichSha = whichSha;
/*如果鍵長於哈希塊大小,將其重置為key = hash (key)。 */
if (key_len > blocksize) {
SHAContext tcontext;
int err = SHAReset(&tcontext, whichSha) ||
SHAInput(&tcontext, key, key_len) ||
SHAResult(&tcontext, tempkey);
if (err != shaSuccess) return err;
key = tempkey;
key_len = hashsize;
}
/*將key與ipad和opad按位異或*/
for (i = 0; i < key_len; i++) {
k_ipad[i] = key[i] ^ 0x36;
context->k_opad[i] = key[i] ^ 0x5c;
}
/*將key填充0直到blocksize並與ipad和opad按位異或 */
for ( ; i < blocksize; i++) {
k_ipad[i] = 0x36;
context->k_opad[i] = 0x5c;
}
/* 開始內層哈希運算 */
ret = SHAReset(&context->shaContext, whichSha) ||
SHAInput(&context->shaContext, k_ipad, blocksize);
return context->Corrupted = ret;
}
接下來輸入將要處理的信息,這個函數接受一個字節數組作為消息的下一處理部分。
int hmacInput(HMACContext *context, const unsigned char *text,int text_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* 報文內容 */
return context->Corrupted =SHAInput(&context->shaContext, text, text_len);
}
最後處理完成全部過程,返回信息摘要。此函數將返回相應大小的消息摘要,具體是多長的信息摘要由具體的SHA算法決定。
int hmacResult(HMACContext *context, uint8_t *digest)
{
int ret;
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* 完成內層哈希運算 */
ret =SHAResult(&context->shaContext, digest) ||
/* 執行外層哈希運算 */
SHAReset(&context->shaContext, context->whichSha) ||
SHAInput(&context->shaContext, context->k_opad,context->blockSize) ||
SHAInput(&context->shaContext, digest, context->hashSize) ||
SHAResult(&context->shaContext, digest);
context->Computed = 1;
return context->Corrupted = ret;
}
4、結果
我們已經實現了HMAC算法,接下來我們對其進行驗證。我們采用簡單的測試,取加密文本為text =“abcd”,設密鑰為key=“123456”,基於SHA-1的HMAC運算結果如下:
基於SHA-256的HMAC運算結果如下:
基於SHA-512的運算結果如下:
我們測試了HMAC-SHA1、HMAC-SHA256、HMAC-512三種情況,與在線HMAC加密算法進行對比計算,記過完全一致。
歡迎關註:
信息摘要算法之五:HMAC算法分析與實現