Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

3.15 哈希与MAC函数:数据的“指纹与印章“

哈希与MAC函数概览

PKCS#11规范第5.12节定义了哈希(Digest)函数,用于计算数据的摘要值。

哈希函数列表:

函数名            作用
─────────────────────────────────────
C_DigestInit      初始化哈希操作
C_Digest          单步哈希计算
C_DigestUpdate    多步哈希中间步骤
C_DigestKey       将密钥加入哈希计算
C_DigestFinal     多步哈希结束

MAC(消息认证码):
─────────────────────────────────────
通过签名函数实现(C_Sign系列)
使用HMAC机制(CKM_SHA256_HMAC等)

哈希函数的基本概念

哈希函数将任意长度数据映射为固定长度的摘要值:

哈希运算示意:

输入(任意长度)          输出(固定长度)
───────────────────────────────────────
"Hello"        → SHA-256 → 32字节摘要
"Hello World"  → SHA-256 → 32字节摘要
1GB视频文件    → SHA-256 → 32字节摘要

特性:
- 单向性:无法从摘要还原原始数据
- 确定性:相同输入产生相同输出
- 抗碰撞性:难以找到不同输入产生相同摘要

C_DigestInit:初始化哈希

函数原型

CK_RV C_DigestInit(
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism
);

常用哈希机制

哈希机制列表:

CKM_SHA_1          SHA-1(160位,已不推荐)
CKM_SHA224         SHA-224(224位)
CKM_SHA256         SHA-256(256位,最常用)
CKM_SHA384         SHA-384(384位)
CKM_SHA512         SHA-512(512位)
CKM_MD5            MD5(128位,已不安全)
CKM_RIPEMD160      RIPEMD-160(160位)

国密哈希(厂商扩展,非PKCS#11 v3.1标准):
CKM_SM3            SM3(256位,中国标准)

注意:SM3等国密机制是厂商对PKCS#11的扩展,并非OASIS PKCS#11 v3.1标准规范的一部分。


单步哈希示例

/* SHA-256哈希计算 */

CK_MECHANISM mechanism = {CKM_SHA256, NULL_PTR, 0};
rv = C_DigestInit(hSession, &mechanism);

CK_BYTE data[] = "Hello, PKCS#11!";
CK_BYTE digest[32];  // SHA-256输出32字节
CK_ULONG digestLen = sizeof(digest);

rv = C_Digest(hSession, data, sizeof(data)-1, digest, &digestLen);

if (rv == CKR_OK) {
    printf("哈希值:");
    for (int i = 0; i < digestLen; i++) {
        printf("%02x", digest[i]);
    }
    printf("\n");
}
// 输出:a1b2c3d4...(32字节的十六进制)

C_Digest:单步哈希

函数原型

CK_RV C_Digest(
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,       // 输入数据
    CK_ULONG ulDataLen,      // 数据长度
    CK_BYTE_PTR pDigest,     // 摘要缓冲区
    CK_ULONG_PTR pulDigestLen // 摘要长度
);

两步调用惯例

/* 先获取摘要长度 */

CK_ULONG digestLen;
rv = C_Digest(hSession, data, dataLen, NULL_PTR, &digestLen);

/* 再获取摘要值 */

CK_BYTE *digest = malloc(digestLen);
rv = C_Digest(hSession, data, dataLen, digest, &digestLen);

多步哈希计算

/* 流式哈希计算(适合大文件) */

CK_MECHANISM mechanism = {CKM_SHA256, NULL_PTR, 0};
rv = C_DigestInit(hSession, &mechanism);

/* 分块处理 */

CK_BYTE buffer[4096];
CK_ULONG bytesRead;

while ((bytesRead = read_file_chunk(buffer, 4096)) > 0) {
    rv = C_DigestUpdate(hSession, buffer, bytesRead);
}

/* 获取最终摘要 */

CK_BYTE digest[32];
CK_ULONG digestLen = sizeof(digest);
rv = C_DigestFinal(hSession, digest, &digestLen);

C_DigestKey:将密钥加入哈希

函数原型

CK_RV C_DigestKey(
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hKey
);

用途:计算密钥的哈希值,用于密钥验证或密钥指纹。

/* 计算密钥的哈希 */

CK_MECHANISM mechanism = {CKM_SHA256, NULL_PTR, 0};
rv = C_DigestInit(hSession, &mechanism);

/* 将密钥加入哈希 */

rv = C_DigestKey(hSession, hSecretKey);

/* 可选:再加入其他数据 */

rv = C_DigestUpdate(hSession, additionalData, dataLen);

/* 获取摘要 */

CK_BYTE digest[32];
CK_ULONG digestLen = sizeof(digest);
rv = C_DigestFinal(hSession, digest, &digestLen);

注意:C_DigestKey计算的是密钥值本身(即使CKA_SENSITIVE=TRUE),这是规范定义的特殊行为。


HMAC:带密钥的哈希

HMAC(Hash-based MAC)使用密钥进行哈希运算,提供消息认证能力。

HMAC vs 普通哈希:

普通哈希:
- 无密钥参与
- 只验证数据完整性
- 任何人都能计算

HMAC:
- 密钥参与运算
- 验证完整性+真实性
- 只有持有密钥者才能计算

HMAC机制

HMAC机制列表:

CKM_SHA_1_HMAC      HMAC-SHA1(已不推荐)
CKM_SHA224_HMAC     HMAC-SHA224
CKM_SHA256_HMAC     HMAC-SHA256(最常用)
CKM_SHA384_HMAC     HMAC-SHA384
CKM_SHA512_HMAC     HMAC-SHA512
CKM_MD5_HMAC        HMAC-MD5(已不安全)

国密HMAC:
CKM_SM3_HMAC        HMAC-SM3

HMAC计算示例

HMAC使用签名函数(C_Sign)实现:

/* HMAC-SHA256计算 */

CK_MECHANISM mechanism = {CKM_SHA256_HMAC, NULL_PTR, 0};
rv = C_SignInit(hSession, &mechanism, hHmacKey);

CK_BYTE message[] = "Message to authenticate";
CK_BYTE mac[32];  // HMAC输出长度与哈希相同
CK_ULONG macLen = sizeof(mac);

rv = C_Sign(hSession, message, sizeof(message)-1, mac, &macLen);

if (rv == CKR_OK) {
    printf("HMAC值:");
    for (int i = 0; i < macLen; i++) {
        printf("%02x", mac[i]);
    }
    printf("\n");
}

HMAC密钥对象

HMAC密钥是特殊的对称密钥对象:

/* 创建HMAC密钥 */

CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;  // 通用密钥类型
CK_BYTE keyId[] = {0x01};
CK_BYTE keyValue[] = {...};  // HMAC密钥值
CK_BBOOL true = CK_TRUE;
CK_BBOOL sign = CK_TRUE;     // 支持签名(HMAC)
CK_BBOOL verify = CK_TRUE;   // 支持验证

CK_ATTRIBUTE keyTemplate[] = {
    {CKA_CLASS, &keyClass, sizeof(keyClass)},
    {CKA_KEY_TYPE, &keyType, sizeof(keyType)},
    {CKA_TOKEN, &true, sizeof(true)},
    {CKA_ID, keyId, sizeof(keyId)},
    {CKA_VALUE, keyValue, sizeof(keyValue)},
    {CKA_SIGN, &sign, sizeof(sign)},
    {CKA_VERIFY, &verify, sizeof(verify)}
};

CK_OBJECT_HANDLE hHmacKey;
rv = C_CreateObject(hSession, keyTemplate, 7, &hHmacKey);

HMAC验证

使用C_Verify验证HMAC:

/* HMAC验证 */

CK_MECHANISM mechanism = {CKM_SHA256_HMAC, NULL_PTR, 0};
rv = C_VerifyInit(hSession, &mechanism, hHmacKey);

CK_BYTE message[] = "Message to authenticate";
CK_BYTE receivedMac[32];
CK_ULONG macLen = 32;

rv = C_Verify(hSession, message, sizeof(message)-1, receivedMac, macLen);

if (rv == CKR_OK) {
    printf("HMAC验证成功,消息真实\n");
} else if (rv == CKR_SIGNATURE_INVALID) {
    printf("HMAC验证失败,消息可能被篡改\n");
}

双重操作:哈希+加密

PKCS#11支持同时进行哈希和加密操作:

/* 同时加密和计算哈希 */

CK_MECHANISM encryptMech = {CKM_AES_CBC, iv, 16};
CK_MECHANISM digestMech = {CKM_SHA256, NULL_PTR, 0};

rv = C_EncryptInit(hSession, &encryptMech, hAesKey);
rv = C_DigestInit(hSession, &digestMech);

/* 同时加密和哈希 */

CK_BYTE data[] = "Data to encrypt and hash";
CK_BYTE ciphertext[64];
CK_ULONG cipherLen = sizeof(ciphertext);
CK_BYTE digest[32];
CK_ULONG digestLen = sizeof(digest);

/* 使用C_DigestEncryptUpdate */

rv = C_DigestEncryptUpdate(hSession, data, sizeof(data)-1,
                           ciphertext, &cipherLen);
// 同时进行加密和哈希计算

/* 结束操作 */

rv = C_EncryptFinal(hSession, ciphertext + cipherLen, &finalCipherLen);
rv = C_DigestFinal(hSession, digest, &digestLen);

C_DigestEncryptUpdate

函数原型

CK_RV C_DigestEncryptUpdate(
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pEncryptedData,
    CK_ULONG_PTR pulEncryptedDataLen
);

同时进行哈希计算和加密,输出加密结果。


C_DecryptDigestUpdate

同时进行解密和哈希计算:

/* 同时解密和计算哈希 */

CK_MECHANISM decryptMech = {CKM_AES_CBC, iv, 16};
CK_MECHANISM digestMech = {CKM_SHA256, NULL_PTR, 0};

rv = C_DecryptInit(hSession, &decryptMech, hAesKey);
rv = C_DigestInit(hSession, &digestMech);

CK_BYTE decrypted[64];
CK_ULONG decryptedLen = sizeof(decrypted);

rv = C_DecryptDigestUpdate(hSession, ciphertext, cipherLen,
                           decrypted, &decryptedLen);
// 解密的同时计算哈希

rv = C_DecryptFinal(hSession, decrypted + decryptedLen, &finalLen);
rv = C_DigestFinal(hSession, digest, &digestLen);

哈希在车载安全中的应用

车载哈希应用场景:

固件完整性验证:
┌─────────────────────────────────────┐
│ Boot ROM                            │
│                                     │
│ 1. 计算固件哈希                     │
│    digest = SHA256(firmware)        │
│                                     │
│ 2. 验证签名                         │
│    verify(publicKey, signature,     │
│            digest)                  │
│                                     │
│ 3. 哈希匹配 → 固件可信              │
└─────────────────────────────────────┘

日志完整性:
┌─────────────────────────────────────┐
│ 日志系统                            │
│                                     │
│ 每条日志:                          │
│ log_entry = {                       │
│   timestamp,                        │
│   message,                          │
│   hmac = HMAC(key, message)         │
│ }                                   │
│                                     │
│ 验证:检查HMAC确保日志未被篡改       │
└─────────────────────────────────────┘

消息认证:
┌─────────────────────────────────────┐
│ V2X通信                             │
│                                     │
│ 发送:                              │
│ message = {                         │
│   data,                             │
│   mac = HMAC(key, data)             │
│ }                                   │
│                                     │
│ 接收验证:                          │
│ verify_mac(key, received_mac)       │
└─────────────────────────────────────┘

哈希算法选择

哈希算法安全性对比:

算法        输出长度    安全性    推荐度
───────────────────────────────────────
MD5         128位       已破解    ❌不推荐
SHA-1       160位       已破解    ❌不推荐
SHA-224     224位       安全      ⚠️可用
SHA-256     256位       安全      ✓推荐
SHA-384     384位       安全      ✓高安全
SHA-512     512位       安全      ✓高安全
SM3         256位       安全      ✓中国标准

选择建议:
- 车载安全:SHA-256(平衡性能和安全)
- 高安全场景:SHA-384/SHA-512
- 中国合规:SM3
- 避免:MD5、SHA-1(已被破解)

小结:哈希与MAC函数的设计智慧

哈希与MAC要点回顾:

1. 哈希函数的单步/多步模式
   - 单步:小数据量,一次计算
   - 多步:大文件流式处理

2. HMAC通过签名函数实现
   - 使用C_Sign/C_Verify
   - 密钥类型:CKK_GENERIC_SECRET

3. 双重操作效率高
   - C_DigestEncryptUpdate:加密同时哈希
   - C_DecryptDigestUpdate:解密同时哈希

4. C_DigestKey特殊行为
   - 可以计算敏感密钥的哈希
   - 用于密钥验证和指纹

5. 算法选择关键
   - 避免MD5、SHA-1
   - 推荐SHA-256、SHA-384
   - 中国场景使用SM3

哈希函数为PKCS#11提供了数据完整性验证能力,HMAC则在此基础上增加了消息认证能力,两者共同构成数据安全的“指纹与印章“。 下一节,我们将学习密钥管理函数——看密钥的生命周期。

【下集预告】

C_GenerateKeyPair怎么生成密钥?

C_DeriveKey怎么派生密钥?

C_WrapKey怎么备份密钥?

下一节,密钥管理函数。