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,則在其後面追加來使其長度達到B。
(2)、將上一步生成的B字長的密鑰字元串與ipad做異或運算。
(3)、將需要處理的數據流text填充至第二步的結果字元串中。
(4)、使用哈希函數H計算上一步中生成的數據流的信息摘要值。
(5)、將第一步生成的B字長密鑰字元串與opad做異或運算。
(6)、再將第四步得到的結果填充到第五步的結果之後。
(7)、使用哈希函數H計算上一步中生成的數據流的信息摘要值,輸出結果就是最終的HMAC值。
由上述描述過程,我們知道HMAC演算法的計算過程實際是對原文做了兩次類似於加鹽處理的哈希過程。
3、代碼實現
前面我們描述了HMAC演算法及其實現過程,接下來我們則將其實現。首先我們定義一個用於保存計算過程上下文的結構:
/**該結構將為HMAC密鑰哈希操作保存上下文信息*/
typedef structHMACContext {
int whichSha; /*所用的SHA演算法*/
int hashSize; /*所用SHA的哈希值大小*/
int blockSize; /*所用SHA塊的大小*/
SHAContext shaContext; /* SHA上下文*/
unsigned chark_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消息摘要。
inthmacReset(HMACContext *context, enum SHAversion whichSha,const unsigned char*key, int key_len)
{
int i, blocksize, hashsize, ret;
unsigned chark_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
k_ipad[i] = key[i] ^ 0x36;
context->k_opad[i] = key[i] ^ 0x5c;
}
/*將key填充直到blocksize並與ipad和opad按位異或*/
for ( ; 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;
}
接下來輸入將要處理的信息,這個函數接受一個位元組數組作為消息的下一處理部分。
inthmacInput(HMACContext *context, const unsigned char *text,int text_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) returncontext->Corrupted = shaStateError;
/*報文內容*/
return context->Corrupted=SHAInput(&context->shaContext, text, text_len);
}
最後處理完成全部過程,返回信息摘要。此函數將返回相應大小的消息摘要,具體是多長的信息摘要由具體的SHA演算法決定。
inthmacResult(HMACContext *context, uint8_t *digest)
{
int ret;
if (!context) return shaNull;
if (context->Corrupted) returncontext->Corrupted;
if (context->Computed) returncontext->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加密演算法進行對比計算,記過完全一致。
歡迎關註:
TAG:木南創智 |