HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。它可以与任何迭代散列函数捆绑使用。
本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。
HMAC相关的头文件在hmac.h中、源文件在crypto/hmac目录中。
主要结构:
struct hmac_ctx_st {
const EVP_MD *md;
EVP_MD_CTX *md_ctx;
EVP_MD_CTX *i_ctx;
EVP_MD_CTX *o_ctx;
typedef struct hmac_ctx_st HMAC_CTX;
这个结构定义了HMAC的运算上下文。主要字段含义:
md —— 摘要算法。
md_ctx —— 摘要算法上下文。
i_ctx —— 这两个结构参与密码填充散列运算。
o_ctx —— 这两个结构参与密码填充散列运算。
其中,EVP_MD的定义为:
struct evp_md_st {
int type;
int pkey_type;
int md_size;
unsigned long flags;
int (*init) (EVP_MD_CTX *ctx);
int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
int (*final) (EVP_MD_CTX *ctx, unsigned char *md);
int (*copy) (EVP_MD_CTX *to, const EVP_MD_CTX *from);
int (*cleanup) (EVP_MD_CTX *ctx);
int block_size;
int ctx_size; /* how big does the ctx->md_data need to be */
/* control function */
int (*md_ctrl) (EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
typedef struct evp_md_st EVP_MD;
这个结束定义了进行通用摘要运算的抽象方法集合。
在1.1.1中,大多数的数据结构已经不再向使用者开放,从封装的角度来看,这是更合理的。如果你在头文件中找不到结构定义,不妨去源码中搜一搜。
主要函数:
HMAC_CTX *HMAC_CTX_new(void);
创建HMAC上下文结构。
void HMAC_CTX_free(HMAC_CTX *ctx);
释放HMAC上下文结构。
int HMAC_Init(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md);
初使化HMAC上下文结构,key和len指定密码。
成功返回1,失败返回0。
int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len);
向HMAC上下文输入字节流。
成功返回1,失败返回0。
int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len);
生成最终的HMAC串。
成功返回1,失败返回0。
成功时len更新为HMAC的长度。
unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
const unsigned char *d, size_t n, unsigned char *md,
unsigned int *md_len);
对于短的字符串,可以使用该函数一次性生成HMAC摘要,简化调用。
在evp.h中,有EVP_MD方法的相关定义:
const EVP_MD *EVP_md5(void);
const EVP_MD *EVP_sha1(void);
const EVP_MD *EVP_sha256(void);
const EVP_MD *EVP_sha512(void);
例用举例:
这个例子演示了使用两种方式生成消息认证码,并对比结果。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
namespace dakuang {}
void printHex(const unsigned char* pBuf, int nLen)
for (int i = 0; i < nLen; ++i)
printf("%02x", pBuf[i]);
printf("\n");
int main(int argc, char* argv[])
char sText[] = "abdefg1234567890";
HMAC_CTX* ctx = HMAC_CTX_new();
HMAC_Init(ctx, "123", 3, EVP_sha1());
for (int i = 0; i < strlen(sText); ++i)
HMAC_Update(ctx, (const unsigned char*)&sText[i], 1);
unsigned char sSHA[20] = {0};
unsigned int nSHALen = 20;
int ret = HMAC_Final(ctx, sSHA, &nSHALen);
printf("ret:%d outlen:%d \n", ret, nSHALen);
printHex(sSHA, 20);
HMAC_CTX_free(ctx);
unsigned char sSHA[20] = {0};
unsigned int nSHALen = 20;
unsigned char* ret = HMAC(EVP_sha1(), "123", 3, (const unsigned char*)sText, strlen(sText), sSHA, &nSHALen);
printf("ret %p \n", ret);
printf("sSHA %p nSHALen:%d \n", sSHA, nSHALen);
printHex(sSHA, 20);
return 0;