4. 实现AES-256-GCM认证加密

任务说明

本案例基于HSM(硬件安全模块)的AES-256-GCM引擎,实现带关联数据(AAD)的认证加密与解密验证。GCM模式同时提供机密性(加密)、完整性(密文校验)与真实性(AAD绑定),适用于车规通信中ECU间安全报文传输。完成本案例后,你将获得一组可直接调用的加解密接口,以及对应的验证方法。

HSM GCM引擎特性

HSM内部集成AES-256-GCM硬件加速器,支持密钥存储在安全密钥槽(非易失)中,明文密钥不出HSM边界。GCM操作需要以下输入参数:

  • 密钥槽ID:指向HSM内已烧录的256位密钥
  • IV(96位):每次加密必须唯一,推荐使用计数器或随机数
  • AAD:需要认证但不加密的关联数据(如报文头、CAN ID)
  • 明文/密文:待保护的数据载荷
  • Tag(128位):认证标签,用于完整性校验

代码实现

以下代码基于HSM标准驱动接口(HAL风格),实现GCM加密与解密验证的完整流程。代码中定义了必要的结构体、初始化函数以及加解密函数,并附带一个自测用例用于验证功能正确性。


#include "hsm_driver.h"
#include <string.h>

/* HSM密钥槽ID(需预先通过安全烧录流程写入) */
#define AES256_GCM_KEY_SLOT  0x04

/* GCM参数定义 */
#define GCM_IV_LEN       12   /* 96位IV */
#define GCM_TAG_LEN      16   /* 128位认证标签 */
#define GCM_AAD_MAX_LEN  64
#define GCM_DATA_MAX_LEN 256

/* 返回状态 */
typedef enum {
    GCM_OK = 0,
    GCM_ERR_KEY_SLOT,
    GCM_ERR_IV_NULL,
    GCM_ERR_TAG_MISMATCH,
    GCM_ERR_HSM_OP_FAIL
} gcm_status_t;

/* GCM操作上下文 */
typedef struct {
    uint8_t iv[GCM_IV_LEN];
    uint8_t aad[GCM_AAD_MAX_LEN];
    uint32_t aad_len;
    uint8_t tag[GCM_TAG_LEN];
} gcm_ctx_t;

/* HSM底层驱动接口(由芯片厂商提供) */
extern int32_t hsm_aes_gcm_encrypt(uint32_t key_slot,
                                   const uint8_t *iv, uint32_t iv_len,
                                   const uint8_t *aad, uint32_t aad_len,
                                   const uint8_t *plaintext, uint32_t pt_len,
                                   uint8_t *ciphertext, uint8_t *tag);
extern int32_t hsm_aes_gcm_decrypt(uint32_t key_slot,
                                   const uint8_t *iv, uint32_t iv_len,
                                   const uint8_t *aad, uint32_t aad_len,
                                   const uint8_t *ciphertext, uint32_t ct_len,
                                   const uint8_t *tag, uint8_t *plaintext);

/* GCM加密:输入明文+AAD,输出密文+Tag */
gcm_status_t gcm_encrypt_packet(const gcm_ctx_t *ctx,
                                const uint8_t *plaintext, uint32_t pt_len,
                                uint8_t *ciphertext) {
    if (ctx == NULL || plaintext == NULL || ciphertext == NULL) {
        return GCM_ERR_IV_NULL;
    }
    if (pt_len == 0 || pt_len > GCM_DATA_MAX_LEN) {
        return GCM_ERR_HSM_OP_FAIL;
    }

    int32_t ret = hsm_aes_gcm_encrypt(AES256_GCM_KEY_SLOT,
                                      ctx->iv, GCM_IV_LEN,
                                      ctx->aad, ctx->aad_len,
                                      plaintext, pt_len,
                                      ciphertext,
                                      (uint8_t *)ctx->tag);
    if (ret != 0) {
        return GCM_ERR_HSM_OP_FAIL;
    }
    return GCM_OK;
}

/* GCM解密验证:输入密文+Tag+AAD,输出明文并校验完整性 */
gcm_status_t gcm_decrypt_packet(const gcm_ctx_t *ctx,
                                const uint8_t *ciphertext, uint32_t ct_len,
                                uint8_t *plaintext) {
    if (ctx == NULL || ciphertext == NULL || plaintext == NULL) {
        return GCM_ERR_IV_NULL;
    }
    if (ct_len == 0 || ct_len > GCM_DATA_MAX_LEN) {
        return GCM_ERR_HSM_OP_FAIL;
    }

    int32_t ret = hsm_aes_gcm_decrypt(AES256_GCM_KEY_SLOT,
                                      ctx->iv, GCM_IV_LEN,
                                      ctx->aad, ctx->aad_len,
                                      ciphertext, ct_len,
                                      ctx->tag,
                                      plaintext);
    if (ret != 0) {
        return GCM_ERR_TAG_MISMATCH;  /* 认证失败:数据被篡改或密钥错误 */
    }
    return GCM_OK;
}

/* 自测用例:验证加解密闭环 */
gcm_status_t gcm_self_test(void) {
    gcm_ctx_t ctx;
    uint8_t plaintext[] = "Secure CAN message payload";
    uint8_t ciphertext[GCM_DATA_MAX_LEN];
    uint8_t decrypted[GCM_DATA_MAX_LEN];
    uint32_t msg_len = strlen((char *)plaintext);

    /* 初始化IV(实际使用中必须唯一,此处用固定值仅作演示) */
    memset(&ctx, 0, sizeof(ctx));
    for (int i = 0; i < GCM_IV_LEN; i++) {
        ctx.iv[i] = (uint8_t)(0xA0 + i);
    }

    /* 设置AAD:模拟CAN报文ID + 发送方ID */
    ctx.aad[0] = 0x1A;  /* CAN ID高字节 */
    ctx.aad[1] = 0x2B;  /* CAN ID低字节 */
    ctx.aad[2] = 0x03;  /* 发送方节点ID */
    ctx.aad_len = 3;

    /* 加密 */
    if (gcm_encrypt_packet(&ctx, plaintext, msg_len, ciphertext) != GCM_OK) {
        return GCM_ERR_HSM_OP_FAIL;
    }

    /* 解密验证 */
    if (gcm_decrypt_packet(&ctx, ciphertext, msg_len, decrypted) != GCM_OK) {
        return GCM_ERR_TAG_MISMATCH;
    }

    /* 验证明文一致性 */
    if (memcmp(plaintext, decrypted, msg_len) != 0) {
        return GCM_ERR_HSM_OP_FAIL;
    }

    return GCM_OK;
}

关键设计说明

  • 密钥管理:密钥已预先烧录在HSM密钥槽0x04中,代码中不出现明文密钥,符合HSM安全边界要求。
  • IV唯一性:GCM安全性依赖IV唯一性。生产环境中建议使用单调计数器或HSM内部随机数发生器生成IV,禁止在相同密钥下重复使用同一IV。
  • AAD绑定:AAD参与认证但不加密。本案例将CAN ID和节点ID作为AAD,确保报文头被篡改时解密失败。
  • Tag校验:解密函数内部完成Tag比对,任何一位密文或AAD被修改都会导致认证失败并返回错误码。

编译与运行


# 交叉编译(以ARM Cortex-M为例)
arm-none-eabi-gcc -c gcm_demo.c -o gcm_demo.o
arm-none-eabi-gcc gcm_demo.o -o gcm_demo.elf

# 烧录后通过调试串口观察自测结果
# 若gcm_self_test()返回GCM_OK,则加解密链路正常

验证方法

在目标硬件上执行gcm_self_test(),观察返回值:

  • 返回GCM_OK:加解密闭环正确,Tag校验通过,AAD绑定生效。
  • 返回GCM_ERR_TAG_MISMATCH:说明解密时Tag比对失败,可能原因包括:密文被篡改、AAD不一致、IV不匹配或密钥槽错误。

额外验证篡改检测:在加密完成后,手动修改ciphertext[0]的任意一位,再调用gcm_decrypt_packet(),此时必须返回GCM_ERR_TAG_MISMATCH,证明完整性保护有效。

AES-256-GCM 认证加密数据流 CPU 应用层 明文数据 AAD (CAN ID+节点) IV (96位) HSM 硬件安全模块 AES-256-GCM 硬件引擎 密钥槽 0x04 (256位) Tag 生成/校验 IV 唯一性检查 明文 + AAD + IV 密文 + Tag (128位) 回传 密文 + Tag + AAD + IV 明文 (认证通过) 或 错误码 安全边界:密钥永不离开HSM 篡改检测:修改密文任意位 → 解密返回 GCM_ERR_TAG_MISMATCH

判定标准

本案例通过标准如下:

  1. gcm_self_test()返回GCM_OK,证明加解密闭环正确;
  2. 人为篡改密文或AAD后,解密函数返回GCM_ERR_TAG_MISMATCH,证明完整性保护有效;
  3. 代码中无明文密钥出现,所有密钥操作均通过HSM密钥槽引用。