引言
最近手头有个项目涉及到HMAC消息认证算法,要求基于国密算法SM3和C实现,即HMAC-SM3; 网上查资料:HMAC-MD5/HMAC-SHA等一大堆java/python的可用接口,却很少看到基于C语言实现的,若是再加上SM3的算法,更是少之又少; 于是乎,自己撸了一个,权当心得记录;
1 何为SM3算法?
相信很多同学听过MD5算法,但听过SM3算法估计会比较少,因为涉及到行业领域;
-
MD5是一种摘要算法:输入任意长度的信息,经过摘要处理,输出为128位(16字节)数据。(别称:数字指纹);
-
SM3算法是国密算法中的一种,对标的是MD5算法;(另外,SM2对标RSA算法,SM4对标DES算法,ZF要求银行等金融领域均改造成国密算法); SM3输出的摘要值为256位(32字节),其安全性更高于MD5;
2 何为HMAC算法?
HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写; 基于MD5/SM3等摘要算法,多融入了一个Key(秘钥数组);HMAC计算得到的完整摘要值与原摘要算法计算的一致;
3 为何要用HMac算法?
以MD5为例展开,比如通过某个镜像网站下载一个文件,下载完成后可通过本地MD5对下载的文件计算MD5值,与该网站上同文件显示的MD5值比对,一不一致就很清楚了。这是不同渠道,各自计算摘要值进行比对的案例场景;
如果要通过同一个渠道发送数据和Hash值的话(比如消息认证码),就要考虑数据和MD5同时被篡改的问题;如果第三方修改了数据,然后进行MD5 Hash,并一块发给接收方,接收方并不能察觉到数据被篡改。
HMAC-MD5就可以用一把发送方和接收方都有的key进行计算,而没有这把key的第三方是无法计算出正确的Hash的,这样就可以防止数据被篡改。
4 HMAC算法流程
以下基于HMAC-SM3说明HMAC的算法流程(其余摘要算法替换掉SM3即可):
在HMAC的定义中用到一个密码散列函数和一个密钥Key。本说明中使用SM3作为对明文进行分组循环压缩的散列函数,明文分组长度为64(byte),散列函数的输出长度为32(byte)。认证密钥K为随机生成。
再定义两个不同的固定字符串iPad和oPad如下("i"和"o"表示内部和外部):
再定义两个不同的固定字符串iPad和oPad如下("i"和"o"表示内部和外部):
●iPad=一个字节(byte)的 0x36重复64次;
●oPad=- 一个字节(byte)的0x5C重复64次
操作步骤如下:
1.在密钥K后面填充0,使其成为长度为64byte的字符串。
2.用第一步得到的 64byte的字符串与iPad作按位异或;
3.将消息Message附加到第二步产生的64byte字符串后面;
4.对第三步产生的数据流用散列函数SM3计算消息摘要;
5.用第一步得到的 64byte的字符串与oPad作按位异或:
6.将第四步生成的消息摘要附加到第五步的64byte字符串之后:
7.对第六步产生的数据流用散列函数SM3计算消息摘要,作为输出
以输入Message为例,作如下操作: H(K XOR oPad, H(K XOR iPad, Message))
其中H为哈希函数,也就是我们,上面的SM3;
5 HMAC-SM3基于C的代码实现
/*
========================================================================
Routine Description:
HMAC using SM3 hash function
Arguments:
key Secret key
key_len The length of the key in bytes
message Message context
message_len The length of message in bytes
macLen Request the length of message authentication code
Return Value:
mac Message authentication code
Note:
None
========================================================================
*/
int HMAC_SM3(
IN UINT8 Key[],
IN UINT KeyLen,
IN const UINT8 Message[],
IN UINT MessageLen,
OUT UINT8 MAC[],
IN UINT MACLen)
{
UINT8 K0[SM3_BLOCK_SIZE];
UINT8 Digest[SM3_DIGEST_SIZE];
UINT index;
UINT ret;
UINT8 *p_hmac_sm3_data=NULL;
UINT hmac_sm3_data_len=0;
UINT8 tmp_buf[SM3_BLOCK_SIZE+SM3_DIGEST_SIZE]={0};
/*
* If the length of K = B(Block size): K0 = K.
* If the length of K > B: hash K to obtain an L byte string,
* then append (B-L) zeros to create a B-byte string K0 (i.e., K0 = H(K) || 00...00).
* If the length of K < B: append zeros to the end of K to create a B-byte string K0
*/
NdisZeroMemory(K0, SM3_BLOCK_SIZE);
if (KeyLen <= SM3_BLOCK_SIZE) {
NdisMoveMemory(K0, Key, KeyLen);
}
else {
ret=mh_sm3(K0, Key, SM3_DIGEST_SIZE);
if(ret!=MH_RET_SM3_SUCCESS){
return RETURN_FAIL;
}
}
/* Exclusive-Or K0 with ipad */
/* ipad: Inner pad; the byte x锟斤拷36锟斤拷 repeated B times. */
for (index = 0; index < SM3_BLOCK_SIZE; index++)
K0[index] ^= 0x36;
/* End of for */
hmac_sm3_data_len=sizeof(K0)+MessageLen;
p_hmac_sm3_data = (unsigned char *)malloc(hmac_sm3_data_len);
if(NULL == p_hmac_sm3_data)
{
return RETURN_FAIL;
}
NdisMoveMemory(p_hmac_sm3_data,K0,sizeof(K0));
NdisMoveMemory(p_hmac_sm3_data+sizeof(K0),Message,MessageLen);
ret=mh_sm3(Digest, p_hmac_sm3_data, hmac_sm3_data_len);
if(ret!=MH_RET_SM3_SUCCESS){
free(p_hmac_sm3_data);
p_hmac_sm3_data = NULL;
return RETURN_FAIL;
}
/* Exclusive-Or K0 with opad and remove ipad */
/* opad: Outer pad; the byte x锟斤拷5c锟斤拷 repeated B times. */
for (index = 0; index < SM3_BLOCK_SIZE; index++)
K0[index] ^= 0x36^0x5c;
/* End of for */
NdisMoveMemory(tmp_buf,K0,sizeof(K0));
NdisMoveMemory(tmp_buf+sizeof(K0),Digest,sizeof(Digest));
ret=mh_sm3(Digest, tmp_buf, sizeof(tmp_buf));
if(ret!=MH_RET_SM3_SUCCESS){
free(p_hmac_sm3_data);
p_hmac_sm3_data = NULL;
return RETURN_FAIL;
}
free(p_hmac_sm3_data);
p_hmac_sm3_data = NULL;
if (MACLen > SM3_DIGEST_SIZE)
NdisMoveMemory(MAC, Digest, SM3_DIGEST_SIZE);
else
NdisMoveMemory(MAC, Digest, MACLen);
return RETURN_SUCC;
} /* End of HMAC_SHA256 */