1. 程式人生 > >密碼學之HMAC

密碼學之HMAC

密碼學之HMAC


MAC(Message Authentication Code,訊息認證碼演算法)是含有金鑰雜湊函式演算法,相容了MD和SHA演算法的特性,並在此基礎上加上了金鑰。因此MAC演算法也經常被稱作HMAC演算法

簡述

 金鑰相關的雜湊運算訊息認證碼,HMAC運算利用雜湊演算法,以一個金鑰和一個訊息為輸入,生成一個訊息摘要作為輸出

HMAC演算法首先它是基於資訊摘要演算法的。目前主要集合了MD和SHA兩大系列訊息摘要演算法。其中MD系列的演算法有HmacMD2、HmacMD4、HmacMD5三種演算法;SHA系列的演算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512五種演算法。

HMAC演算法除了需要資訊摘要演算法外,還需要一個金鑰。HMAC的金鑰可以是任何長度,如果金鑰的長度超過了摘要演算法資訊分組的長度,則首先使用摘要演算法計算金鑰的摘要作為新的金鑰。一般不建議使用太短的金鑰,因為金鑰的長度與安全強度是相關的。通常選取金鑰長度不小於所選用摘要演算法輸出的資訊摘要的長度

HMAC演算法表示

演算法公式 :
HMAC(K,M)=H(K⊕opad∣H(K⊕ipad∣M))
H 代表所採用的HASH演算法(如SHA-256)
K 代表認證密碼
Ko 代表HASH演算法的密文
M 代表一個訊息輸入
B 代表H中所處理的塊大小,這個大小是處理塊大小,而不是輸出hash的大小
如,SHA-1和SHA-256 B = 64
SHA-384和SHA-512 B = 128
L 表示hash的大小
Opad 用0x5c重複B次
Ipad 用0x36重複B次
Apad 用0x878FE1F3重複(L/4)次

運算步驟

  1. 在金鑰K後面新增0來建立一個字長為B的字串。(例如,如果K的字長是20位元組,B=64位元組,則K後會加入44個零位元組0x00)
  2. 將上一步生成的B字長的字串與ipad做異或運算。
  3. 將資料流text填充至第二步的結果字串中。
  4. 用H作用於第三步生成的資料流。
  5. 將第一步生成的B字長字串與opad做異或運算。
  6. 再將第四步的結果填充進第五步的結果中。
  7. 用H作用於第六步生成的資料流,輸出最終結果
    由上述描述過程,我們知道HMAC演算法的計算過程實際是對原文做了兩次類似於加鹽處理的雜湊過程。

應用

hmac主要應用在身份驗證中,它的使用方法是這樣的:
(1) 客戶端發出登入請求(假設是瀏覽器的GET請求)
(2) 伺服器返回一個隨機值,並在會話中記錄這個隨機值
(3) 客戶端將該隨機值作為金鑰,使用者密碼進行hmac運算,然後提交給伺服器
(4) 伺服器讀取使用者資料庫中的使用者密碼和步驟2中傳送的隨機值做與客戶端一樣的hmac運算,然後與使用者傳送的結果比較,如果結果一致則驗證使用者合法
在這個過程中,可能遭到安全攻擊的是伺服器傳送的隨機值和使用者傳送的hmac結果,而對於截獲了這兩個值的黑客而言這兩個值是沒有意義的,絕無獲取使用者密碼的可能性,隨機值的引入使hmac只在當前會話中有效,大大增強了安全性和實用性。大多數的語言都實現了hmac演算法,比如php的mhash、

python的hmac.py、java的MessageDigest類,在web驗證中使用hmac也是可行的,用js進行md5運算的速度也是比較快的

安全性淺析

由上面的介紹,我們可以看出,HMAC演算法更象是一種加密演算法,它引入了金鑰,其安全性已經不完全依賴於所使用的HASH演算法,安全性主要有以下幾點保證:
(1) 使用的金鑰是雙方事先約定的,第三方不可能知道。由3.2介紹的應用流程可以看出,作為非法截獲資訊的第三方,能夠得到的資訊只有作為“挑戰”的隨機數和作為“響應”的HMAC結果,無法根據這兩個資料推算出金鑰。由於不知道金鑰,所以無法仿造出一致的響應

缺點

在實際應用中,金鑰一般在註冊的時候服務端提供, 以後不再提供,大大增強了安全性,金鑰發出後,客戶端和服務端各在本地儲存一份.但這樣有一個缺點:即如果客戶端換手機登入,金鑰就不存在了
下面說說解決思路:
服務端:金鑰,加密之後的密碼
客戶端登入思路:

  1. 使用者輸入賬號,密碼
  2. 本地查詢金鑰,如果沒有金鑰,向伺服器獲取
  3. 如果應用添加了裝置鎖,需要解鎖(類似qq登入),舊手機同意登入驗證後才給新手機發送密碼進行登入
  4. 手機銀行等登入(密碼+金鑰+時間(年月日時分)),服務端獲取當前時間和前一分鐘進行加密,和客戶端發來的進行比對

在objective中應用的程式碼示例

 NSString* key = @"secret";
 NSString* data = @"Message";

 const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
 const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
 unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
 CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
 NSData *hash = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

 NSLog(@"%@", hash);