如果要通過同一個渠道發送數據和散列值的話(比如消息認證碼),就要考慮數據和MD5同時被篡改的問題,如果第三方修改了數據,然后進行MD5散列,并一塊發給接收方,接收方并不能察覺到數據被篡改。HMAC-MD5就可以用一把發送方和接收方都有的key進行計算,而沒有這把key的第三方是無法計算出正確的散列值的,這樣就可以防止數據被篡改。
以下是分析節選,對于更詳細的描述可以查閱RFC2104文檔。
HMAC需要一個加密用散列函數(表示為H)和一個密鑰K。
假設H是一個將數據塊用一個基本的迭代壓縮函數來加密的散列函數。
用B來表示數據塊的長。(以上說提到的散列函數的分割數據塊長B=64),用L來表示散列函數的輸出數據長(MD5中L=16,SHA—1中L=20)。
密鑰的長度可以是小于等于數據塊長的任何正整數值。應用程序中使用的密鑰長度若是比B大,則首先用使用散列 函數H作用于它,然后用H輸出的L長度字符串作為在HMAC中實際使用的密鑰。
一般情況下,推薦的最小密鑰K長度是L長。(與H的輸出數據長度相等)。 我們將定義兩個固定且不同的字符串ipad,opad:
ipad = the byte 0x36 repeated B times
opad = the byte 0x5C repeated B times
#計算‘text'的HMAC:
H( K XOR opad, H(K XOR ipad, text))
即為以下步驟:
(1) append zeros to the end of K to create a B byte string
(e.g., if K is of length 20 bytes and B=64, then K will be
appended with 44 zero bytes 0x00)
(2) XOR (bitwise exclusive-OR) the B byte string computed in step
(1) with ipad
(3) append the stream of data 'text' to the B byte string resulting
from step (2)
(4) apply H to the stream generated in step (3)
(5) XOR (bitwise exclusive-OR) the B byte string computed in
step (1) with opad
(6) append the H result from step (4) to the B byte string
resulting from step (5)
(7) apply H to the stream generated in step (6) and output
the result
void hmac_md5(char* out, char* data, int dlen, char* key, int klen)
{
(1) 在密鑰key后面添加0來創建一個長為B(64字節)的字符串(str)。
(2) 將上一步生成的字符串(str)與ipad(0x36)做異或運算,形成結果字符串(istr)。
(3) 將數據流data附加到第二步的結果字符串(istr)的末尾。
(4) 做md5運算于第三步生成的數據流(istr)。
(5) 將第一步生成的字符串(str)與opad(0x5c)做異或運算,形成結果字符串(ostr)。
(6) 再將第四步的結果(istr)附加到第五步的結果字符串(ostr)的末尾。
(7) 做md5運算于第六步生成的數據流(ostr),輸出最終結果(out)。
}
#注:如果第一步中,key的長度klen大于64字節,則先進行md5運算,使其長度klen=16字節。
PHP實現:
<?php
function custom_hmac($algo, $data, $key, $raw_output = false)
{
$algo = strtolower($algo);
$pack = 'H'.strlen($algo('test'));
$size = 64;
$opad = str_repeat(chr(0x5C), $size);
$ipad = str_repeat(chr(0x36), $size);
if (strlen($key) > $size) {
$key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
} else {
$key = str_pad($key, $size, chr(0x00));
}
for ($i = 0; $i < strlen($key) - 1; $i++) {
$opad[$i] = $opad[$i] ^ $key[$i];
$ipad[$i] = $ipad[$i] ^ $key[$i];
}
$output = $algo($opad.pack($pack, $algo($ipad.$data)));
return ($raw_output) ? pack($pack, $output) : $output;
}
?>
Example Use:
<?php
custom_hmac('sha1', 'Hello, world!', 'secret', true);
?>
Java 實例:
public static String hmacSha1(String value, String key) {
try {
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = key.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(value.getBytes());
// Convert raw bytes to Hex
byte[] hexBytes = new Hex().encode(rawHmac);
// Covert array of Hex bytes to a String
return new String(hexBytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
參考:
http://www.ietf.org/rfc/rfc2104.txt
http://www.cnblogs.com/soundcode/p/3802344.html
http://www.ftsafe.com.cn/service/kbase/infomation-2
http://rc3.org/2011/12/02/using-hmac-to-authenticate-web-service-requests/
http://stackoverflow.com/questions/6312544/hmac-sha1-how-to-do-it-properly-in-java
其它(hmac 為什么要pad):
http://blog.sibo.me/2014/05/14/hashing-security.html
http://drops.wooyun.org/papers/1066
http://blog.suchasplus.com/2011/03/HMAC-and-Password-based-authentication-protocol-memo.html
http://www.programgo.com/article/87062287928/
http://www.cnblogs.com/voipman/p/5320237.html
https://www.zhihu.com/question/26576521
哈希長度擴展攻擊解析
http://www.2cto.com/Article/201405/298779.html
https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks