OpenSSL之消息认证码HMAC用法

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;