对HMAC-SHA256算法作的整理。
HMAC算法
HMAC是Hash-based Message Authentication Code的缩写,意为基于哈希运算的消息认证码。基诞生目的是为了确保网络中报文的完整性以及信息来源的身份验证。其中有几个关键组成部分:
- 哈希函数(Hash):用以将任意长度的消息映射成为定长的哈希值。
- 密钥(key):与原始消息组合后通过哈希函数,以起到身份验证功能。
- 原始消息(message):将被处理的消息。
- ipad:值为00110110(0x36)的循环,长度为Hash函数的分组长度。
- opad:值为01011100(0x5c)的循环,长度为Hash函数的分组长度。
HMAC算法描述为:
- 对key值进行填充,形成padded-key,填充方法如下:
- 若key的长度小于Hash函数的分组长度,在其后用0填充至Hash函数分组长度。
- 若key的长度大于Hash函数的分组长度,使用Hash(key)生成padded-key。
- 将生成的padded-key分别与ipad/opad进行XOR运算,得到ipad-key和opan-key。
- 将ipad-key与message首尾相接(ipad-key在message前),进行Hash(ipad-key+message)运算,得到hash1。
- 将得到的opad-key与hash1首尾相接,进行Hash(opad-key+hash1)运算,就得到了HMAC值。
伪码描述:
1 | input: key, message, Hash |
SHA256 算法
SHA是Secure Hash Algorithm的缩写,是一个由美国国家安全局研发的算法族。这些算法大体结构相似,但在性能,数值范围与安全性上存在差别。SHA256算法是其中较为广为人知的一个算法,接受一个最大长度为(2^64 - 1)bit的消息,输出一个256bit长的哈希值。SHA256算法非常安全,目前还没有对SHA256算法的成功碰撞记录。
这个算法有几个关键组成部分:
8个哈希初值:对自然界中前8个质数的平方根小数部分取前32个bit取得。
1
20x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a
,0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd1964个常数:对自然界中前64个质数的立方根小数部分取前32个bit取得。
1
2
3
4
5
6
7
8
9
10
11
12
130x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b
,0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01
,0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7
,0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc
,0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152
,0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147
,0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc
,0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85
,0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819
,0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08
,0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f
,0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208
,0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2原始消息(message):将被处理的消息。
分段长度(chunk_size):512bit
运算字数(word_count):64
函数CROR(x, n) = x循环右移n位
函数S0(x) = CROR(x, 7) ^ CROR(x, 18) ^ (x >> 3)
函数S1(x) = CROR(x, 17) ^ CROR(x, 19) ^ (x >> 10)
函数EP0(x) = CROR(x, 2) ^ CROR(x, 13) ^ CROR(x, 22)
函数EP1(x) = CROR(x, 6) ^ CROR(x, 11) ^ CROR(x, 25)
函数CH(x, y, z) = (x & y) ^ ((~x) & z)
函数MAJ(x, y, z) = ((x & y) ^ (x & z) ^ (y & z))
算法描述如下:
对message进行预处理:
在message后填充1位1,然后填充若干位0,直到message的长度(bit)对512取模等于448(message.size % chunk_size = 448)。
- 不管message本来长度是多少,都要先填充1位1。也就是说即使message的长度对512取模已经等于448了,还是要填充1位1,之后再填充511位0使其长度重新符合要求。
- 为什么是448?因为下一步要填充一个64bit的数,448+64等于分段长度512。
使用一个64bit长的无符号整型数以大端字节序在message后填充原始message的长度。
字节序,对于长度超过1字节的数据,在内存中的存储有两种顺序:
- 低地址存储低位字节,高地址存储高位字节,称为大端字节序。
- 低地址存储高位字节,高地址存储低信字节,称为小端字节序。
例如,对于0x1234,若使用内存地址0x01, 0x02存储这两个字节,表现为:
1
2
3
4
5
6
7// 大端序
0x01: 0x12
0x02: 0x34
// 小端序
0x01: 0x34
0x02: 0x12值得一提的是,对无论大端序还是小端序,对0x1234取地址都将得到0x01。
使用一个长度为8的数组H[]保存8个哈希初值。
使用一个长度为64的数组k[]保存64个常数。
将预处理后的message分割为若干长度为chunk_size的chunk。
依序对每个chunk进行下列处理:
建立一个大小为word_count(64)的数组w[]
将chunk分割为16个长度为32bit的word,存储在w[0]-w[15]中。
- 注意将每一个word转换为机器字节序,否则位运算会出问题。
对i从16到63进行循环:
- w[i] = w[i - 16] + S0(w[i - 15]) + w[i - 7] + S1(w[i - 2])
使用创建H[]的拷贝h[]
对i从0到63进行循环,根据w[]和k[]计算h[]中的hash值:
t1 = h[7] + EP1(h[4]) + CH(h[4], h[5], h[6]) + k[i] + w[i]
t2 = EP0(h[0]) + MAJ(h[0], h[1], h[2])
对i从7到1循环:
- if (i == 4) then h[i] = h[i - 1] + t1 else h[i] = h[i - 1]
h[0] = t1 + t2
更改H[]供下一个chunk使用:
- H[] += h[]
将最终得到的H[]按大端字节序首尾相接,即形成最终的256bit哈希值。
伪码描述:
1 | input: message |
HMAC_SHA256算法实现
HMAC_SHA256算法就是将SHA256算法作为Hash函数的HMAC算法。简单组合就可得到。
C++实现
理解了上述内容后使用C++实现出来还是很简单的。
1 | /********************************************* |