標籤:cat 驗證 分享圖片 contex 訊息摘要演算法 set err ike 就是
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演算法分析與實現