跳到主要内容

消息认证码计算HMAC(C/C++)

HMAC通过指定摘要算法,以通信双方共享密钥与消息作为输入,生成消息认证码用于检验传递报文的完整性。HMAC在消息摘要算法的基础上增加了密钥的输入,确保了信息的正确性。生成的消息认证码为固定长度。

开发步骤

在调用update接口传入数据时,可以一次性传入,也可以把数据人工分段传入。对于同一段数据而言,是否分段,计算结果没有差异。对于数据量较大的数据,开发者可以根据实际需求选择是否分段传入。

下面分别提供两种方式的示例代码。

HMAC(一次性传入)

  1. 调用OH_CryptoSymKeyGenerator_CreateOH_CryptoSymKeyGenerator_Generate生成密钥算法为HMAC的对称密钥(symKey)。
  2. 调用OH_CryptoMac_Create,指定字符串参数'HMAC',创建MAC算法为HMAC的MAC生成器。
  3. 调用OH_CryptoMac_SetParam,指定参数CRYPTO_MAC_DIGEST_NAME_STR,设置摘要算法名称。
  4. 调用OH_CryptoMac_Init,指定共享对称密钥(symKey),初始化MAC对象。
  5. 调用OH_CryptoMac_Update,传入自定义消息,进行消息认证码计算。
  6. 调用OH_CryptoMac_Final,获取MAC计算结果。
  7. 调用OH_CryptoMac_GetLength,获取MAC消息认证码的长度,单位为字节。
#include "CryptoArchitectureKit/crypto_architecture_kit.h"
#include <cstdio>
#include <cstring>

static OH_CryptoSymKey *GenerateHmacKey(const char *algoName)
{
OH_CryptoSymKeyGenerator *keyGen = nullptr;
OH_Crypto_ErrCode ret = OH_CryptoSymKeyGenerator_Create(algoName, &keyGen);
if (ret != CRYPTO_SUCCESS) {
return nullptr;
}
OH_CryptoSymKey *keyCtx = nullptr;
ret = OH_CryptoSymKeyGenerator_Generate(keyGen, &keyCtx);
OH_CryptoSymKeyGenerator_Destroy(keyGen);
if (ret != CRYPTO_SUCCESS) {
return nullptr;
}
return keyCtx;
}

static OH_Crypto_ErrCode CreateHmacContext(OH_CryptoSymKey *keyCtx, OH_CryptoMac **ctx)
{
OH_Crypto_ErrCode ret = OH_CryptoMac_Create("HMAC", ctx);
if (ret != CRYPTO_SUCCESS) {
return ret;
}

// 设置摘要算法名称为SM3。
const char *digestName = "SM3";
Crypto_DataBlob digestNameData = {
.data = reinterpret_cast<uint8_t *>(const_cast<char *>(digestName)),
.len = strlen(digestName)
};
ret = OH_CryptoMac_SetParam(*ctx, CRYPTO_MAC_DIGEST_NAME_STR, &digestNameData);
if (ret != CRYPTO_SUCCESS) {
OH_CryptoMac_Destroy(*ctx);
return ret;
}

// 初始化HMAC计算。
ret = OH_CryptoMac_Init(*ctx, keyCtx);
if (ret != CRYPTO_SUCCESS) {
OH_CryptoMac_Destroy(*ctx);
return ret;
}

return CRYPTO_SUCCESS;
}

static OH_Crypto_ErrCode UpdateHmacData(OH_CryptoMac *ctx)
{
// 一次性传入所有数据。
const char *message = "hmacTestMessage";
Crypto_DataBlob input = {
.data = reinterpret_cast<uint8_t *>(const_cast<char *>(message)),
.len = strlen(message)
};
OH_Crypto_ErrCode ret = OH_CryptoMac_Update(ctx, &input);
if (ret != CRYPTO_SUCCESS) {
return ret;
}

return CRYPTO_SUCCESS;
}

static OH_Crypto_ErrCode FinalizeHmac(OH_CryptoMac *ctx, Crypto_DataBlob *out, uint32_t *macLen)
{
// 完成HMAC计算并获取结果。
OH_Crypto_ErrCode ret = OH_CryptoMac_Final(ctx, out);
if (ret != CRYPTO_SUCCESS) {
return ret;
}

// 获取HMAC值的长度。
ret = OH_CryptoMac_GetLength(ctx, macLen);
if (ret != CRYPTO_SUCCESS) {
OH_Crypto_FreeDataBlob(out);
return ret;
}

return CRYPTO_SUCCESS;
}

OH_Crypto_ErrCode doTestHmacOnce()
{
OH_CryptoSymKey *keyCtx = nullptr;
OH_CryptoMac *ctx = nullptr;
Crypto_DataBlob out = {0};
OH_Crypto_ErrCode ret = CRYPTO_SUCCESS;
uint32_t macLen = 0;

// 生成HMAC密钥,使用SM3作为摘要算法。
keyCtx = GenerateHmacKey("HMAC|SM3");
if (keyCtx == nullptr) {
ret = CRYPTO_OPERTION_ERROR;
goto cleanup;
}

// 创建HMAC上下文。
ret = CreateHmacContext(keyCtx, &ctx);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

// 一次性传入所有数据。
ret = UpdateHmacData(ctx);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

// 完成HMAC计算。
ret = FinalizeHmac(ctx, &out, &macLen);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

printf("HMAC calculation success, length: %u\n", macLen);

cleanup:
// 清理资源。
OH_Crypto_FreeDataBlob(&out);
OH_CryptoMac_Destroy(ctx);
OH_CryptoSymKey_Destroy(keyCtx);
return ret;
}

HMAC(分段传入)

与一次性传入的步骤基本相同,区别在于多次调用OH_CryptoMac_Update来处理分段数据。

#include "CryptoArchitectureKit/crypto_architecture_kit.h"
#include <cstdio>
#include <cstring>

static OH_CryptoSymKey *GenerateHmacKey(const char *algoName)
{
OH_CryptoSymKeyGenerator *keyGen = nullptr;
OH_Crypto_ErrCode ret = OH_CryptoSymKeyGenerator_Create(algoName, &keyGen);
if (ret != CRYPTO_SUCCESS) {
return nullptr;
}
OH_CryptoSymKey *keyCtx = nullptr;
ret = OH_CryptoSymKeyGenerator_Generate(keyGen, &keyCtx);
OH_CryptoSymKeyGenerator_Destroy(keyGen);
if (ret != CRYPTO_SUCCESS) {
return nullptr;
}
return keyCtx;
}

static OH_Crypto_ErrCode CreateHmacContext(OH_CryptoSymKey *keyCtx, OH_CryptoMac **ctx)
{
OH_Crypto_ErrCode ret = OH_CryptoMac_Create("HMAC", ctx);
if (ret != CRYPTO_SUCCESS) {
return ret;
}

// 设置摘要算法名称为SM3。
const char *digestName = "SM3";
Crypto_DataBlob digestNameData = {
.data = reinterpret_cast<uint8_t *>(const_cast<char *>(digestName)),
.len = strlen(digestName)
};
ret = OH_CryptoMac_SetParam(*ctx, CRYPTO_MAC_DIGEST_NAME_STR, &digestNameData);
if (ret != CRYPTO_SUCCESS) {
OH_CryptoMac_Destroy(*ctx);
return ret;
}

// 初始化HMAC计算。
ret = OH_CryptoMac_Init(*ctx, keyCtx);
if (ret != CRYPTO_SUCCESS) {
OH_CryptoMac_Destroy(*ctx);
return ret;
}

return CRYPTO_SUCCESS;
}

static OH_Crypto_ErrCode ProcessHmacSegments(OH_CryptoMac *ctx)
{
// 分段传入数据。
const char *message = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee";
size_t messageLen = strlen(message);
size_t segmentSize = 20; // 每段20字节。

for (size_t i = 0; i < messageLen; i += segmentSize) {
size_t currentSize = (i + segmentSize <= messageLen) ? segmentSize : (messageLen - i);
Crypto_DataBlob segment = {
.data = reinterpret_cast<uint8_t *>(const_cast<char *>(message + i)),
.len = currentSize
};
OH_Crypto_ErrCode ret = OH_CryptoMac_Update(ctx, &segment);
if (ret != CRYPTO_SUCCESS) {
return ret;
}
}

return CRYPTO_SUCCESS;
}

static OH_Crypto_ErrCode FinalizeHmac(OH_CryptoMac *ctx, Crypto_DataBlob *out, uint32_t *macLen)
{
// 完成HMAC计算并获取结果。
OH_Crypto_ErrCode ret = OH_CryptoMac_Final(ctx, out);
if (ret != CRYPTO_SUCCESS) {
return ret;
}

// 获取HMAC值的长度。
ret = OH_CryptoMac_GetLength(ctx, macLen);
if (ret != CRYPTO_SUCCESS) {
OH_Crypto_FreeDataBlob(out);
return ret;
}

return CRYPTO_SUCCESS;
}

OH_Crypto_ErrCode doTestHmacBySegments()
{
OH_CryptoSymKey *keyCtx = nullptr;
OH_CryptoMac *ctx = nullptr;
Crypto_DataBlob out = {0};
OH_Crypto_ErrCode ret = CRYPTO_SUCCESS;
uint32_t macLen = 0;

// 生成HMAC密钥,使用SM3作为摘要算法。
keyCtx = GenerateHmacKey("HMAC|SM3");
if (keyCtx == nullptr) {
ret = CRYPTO_OPERTION_ERROR;
goto cleanup;
}

// 创建HMAC上下文。
ret = CreateHmacContext(keyCtx, &ctx);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

// 分段处理数据。
ret = ProcessHmacSegments(ctx);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

// 完成HMAC计算。
ret = FinalizeHmac(ctx, &out, &macLen);
if (ret != CRYPTO_SUCCESS) {
goto cleanup;
}

printf("HMAC calculation success, length: %u\n", macLen);

cleanup:
// 清理资源。
OH_Crypto_FreeDataBlob(&out);
OH_CryptoMac_Destroy(ctx);
OH_CryptoSymKey_Destroy(keyCtx);
return ret;
}