基于C实现HMAC-MD5和HMAC-SM3

引言

最近手头有个项目涉及到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 */

6 需要完整HMAC-SM3的C源码的同学可在公众号发送关键字"SM3"获取

在这里插入图片描述

以下是基于 Java 的 Mac 类实现 HMAC-MD5 算法的示例代码: ```java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class HmacMd5 { public static void main(String[] args) { String message = "Hello, world!"; String key = "secret"; String hmac = hmacMd5(message, key); System.out.println(hmac); } public static String hmacMd5(String message, String key) { try { // 创建 Mac 对象 Mac mac = Mac.getInstance("HmacMD5"); // 创建 SecretKeySpec 对象 SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5"); // 初始化 Mac 对象 mac.init(keySpec); // 计算 HMAC 值 byte[] hmacBytes = mac.doFinal(message.getBytes()); // 对 HMAC 值进行 Base64 编码 return Base64.getEncoder().encodeToString(hmacBytes); } catch (NoSuchAlgorithmException | InvalidKeyException e) { e.printStackTrace(); return null; } } } ``` 在示例代码中,我们通过调用 `hmacMd5` 方法来计算 HMAC-MD5 值。该方法接受两个参数:`message` 表示要计算 HMAC 值的消息,`key` 表示 HMAC 密钥。在方法内部,我们首先创建 Mac 对象,然后创建 SecretKeySpec 对象,并使用 HMAC 密钥初始化 Mac 对象。接着,我们调用 Mac 对象的 `doFinal` 方法来计算 HMAC 值,并将其转换为 Base64 编码的字符串返回。最后,在 `main` 方法中,我们演示了如何使用该方法来计算 HMAC-MD5 值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慢慢Coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值