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.14 签名验证函数:密码世界的“签名盖章“

为什么需要签名?

在现实生活中,签名是什么?

签名的本质

  • 证明“是我签署的“(身份认证)
  • 证明“内容未被篡改“(完整性)
  • 证明“我已同意此内容“(不可否认)

在数字世界,数字签名实现了同样的功能:

  • 身份认证:签名证明了消息来自哪个密钥持有者
  • 完整性:签名证明了消息未被篡改
  • 不可否认:签名者无法否认曾签署此消息

签名的基本流程

数字签名有两个操作:

签名流程:

签名者(私钥持有者):
┌─────────────────────────────────────┐
│                                     │
│  消息 → 哈希 → 用私钥签名 → 签名值   │
│                                     │
└─────────────────────────────────────┘
                │
                │ 发送:消息 + 签名值 + 公钥
                ▼
验证者(公钥持有者):
┌─────────────────────────────────────┐
│                                     │
│  消息 + 签名值 + 公钥 → 验证签名     │
│                                     │
│  结果:有效 or 无效                  │
│                                     │
└─────────────────────────────────────┘

PKCS#11的签名函数

PKCS#11规范第5.13节定义了签名函数:

函数描述
C_SignInit初始化签名操作
C_Sign单步签名(消息较短)
C_SignUpdate多步签名,添加更多数据
C_SignFinal多步签名,完成签名
C_SignRecoverInit初始化可恢复签名
C_SignRecover可恢复签名(签名包含消息)

C_SignInit:准备签名

功能:初始化签名操作,指定签名密钥和签名机制。

原型

CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
                  CK_MECHANISM_PTR pMechanism,
                  CK_OBJECT_HANDLE hKey);

参数

  • hSession:Session句柄
  • pMechanism:签名机制(如CKM_RSA_PKCS)
  • hKey:私钥句柄

示例

CK_MECHANISM mechanism = {CKM_RSA_PKCS, NULL_PTR, 0};
CK_OBJECT_HANDLE hPrivateKey;
CK_RV rv;

// 查找私钥
// ...

// 初始化签名
rv = C_SignInit(hSession, &mechanism, hPrivateKey);
if (rv != CKR_OK) {
    printf("签名初始化失败:%08x\n", rv);
    return rv;
}

注意事项

  • 必须使用私钥(CKO_PRIVATE_KEY)
  • 必须在已登录Session中(私钥通常是CKA_PRIVATE = TRUE)
  • 一个Session只能有一个活跃的签名操作

C_Sign:单步签名

功能:一次性签名整个消息。

原型

CK_RV C_Sign(CK_SESSION_HANDLE hSession,
              CK_BYTE_PTR pData,
              CK_ULONG ulDataLen,
              CK_BYTE_PTR pSignature,
              CK_ULONG_PTR pulSignatureLen);

参数

  • hSession:Session句柄
  • pData:待签名数据
  • ulDataLen:数据长度
  • pSignature:签名值输出缓冲区
  • pulSignatureLen:签名值长度

两步调用模式

CK_BYTE data[] = "Hello, PKCS#11!";
CK_BYTE signature[256];
CK_ULONG signatureLen = 256;
CK_RV rv;

// 第一步:获取签名长度
rv = C_Sign(hSession, data, sizeof(data)-1, NULL_PTR, &signatureLen);
if (rv != CKR_OK) {
    printf("获取签名长度失败:%08x\n", rv);
    return rv;
}

// 第二步:执行签名
rv = C_Sign(hSession, data, sizeof(data)-1, signature, &signatureLen);
if (rv == CKR_OK) {
    printf("签名成功,长度:%lu字节\n", signatureLen);
    // 打印签名值(十六进制)
    for (CK_ULONG i = 0; i < signatureLen; i++) {
        printf("%02x", signature[i]);
    }
    printf("\n");
}

C_SignUpdate + C_SignFinal:多步签名

功能:分批签名大数据。

适用场景

  • 文件签名(文件可能很大)
  • 流式数据签名(数据持续到达)

示例

CK_BYTE chunk1[] = "First chunk of data...";
CK_BYTE chunk2[] = "Second chunk of data...";
CK_BYTE chunk3[] = "Third chunk of data...";
CK_BYTE signature[256];
CK_ULONG signatureLen = 256;
CK_RV rv;

// 初始化签名
rv = C_SignInit(hSession, &mechanism, hPrivateKey);

// 分批添加数据
rv = C_SignUpdate(hSession, chunk1, sizeof(chunk1)-1);
rv = C_SignUpdate(hSession, chunk2, sizeof(chunk2)-1);
rv = C_SignUpdate(hSession, chunk3, sizeof(chunk3)-1);

// 完成签名
rv = C_SignFinal(hSession, signature, &signatureLen);
if (rv == CKR_OK) {
    printf("多步签名成功\n");
}

签名机制的选择

PKCS#11支持多种签名机制(规范第6章):

RSA签名机制

机制描述特点
CKM_RSA_PKCSPKCS#1 v1.5签名最常用,兼容性好
CKM_RSA_PKCS_PSSRSASSA-PSS签名更安全,抗选择明文攻击
CKM_RSA_X_509X.509原始签名无填充,不推荐

ECDSA签名机制

| 机制 | 描述 | |:—|:—|:—| | CKM_ECDSA | ECDSA签名 | | CKM_ECDSA_SHA1 | SHA-1哈希后ECDSA签名 | | CKM_ECDSA_SHA256 | SHA-256哈希后ECDSA签名 |

使用示例

/* RSA PKCS#1 v1.5签名 */
CK_MECHANISM rsaPkcsMech = {CKM_RSA_PKCS, NULL_PTR, 0};

/* RSA PSS签名(需要参数) */
CK_RSA_PKCS_PSS_PARAMS pssParams = {CKM_SHA256, CKG_MGF1_SHA256, 32};
CK_MECHANISM rsaPssMech = {CKM_RSA_PKCS_PSS, &pssParams, sizeof(pssParams)};

/* ECDSA签名 */
CK_MECHANISM ecdsaMech = {CKM_ECDSA, NULL_PTR, 0};

/* ECDSA with SHA256 */
CK_MECHANISM ecdsaSha256Mech = {CKM_ECDSA_SHA256, NULL_PTR, 0};

验证函数

PKCS#11规范第5.15节定义了验证函数:

| 函数 | 描述 | |:—|:—|:—| | C_VerifyInit | 初始化验证操作 | | C_Verify | 单步验证 | | C_VerifyUpdate | 多步验证,添加更多数据 | | C_VerifyFinal | 多步验证,完成验证 | | C_VerifyRecoverInit | 初始化可恢复验证 | | C_VerifyRecover | 可恢复验证 |


C_Verify:验证签名

功能:验证签名是否有效。

原型

CK_RV C_Verify(CK_SESSION_HANDLE hSession,
                CK_BYTE_PTR pData,
                CK_ULONG ulDataLen,
                CK_BYTE_PTR pSignature,
                CK_ULONG ulSignatureLen);

返回值

  • CKR_OK:签名有效
  • CKR_SIGNATURE_INVALID:签名无效
  • CKR_SIGNATURE_LEN_RANGE:签名长度错误

示例

CK_BYTE data[] = "Hello, PKCS#11!";
CK_BYTE signature[256];
CK_ULONG signatureLen;
CK_OBJECT_HANDLE hPublicKey;
CK_MECHANISM mechanism = {CKM_RSA_PKCS, NULL_PTR, 0};
CK_RV rv;

// 查找公钥
// ...

// 初始化验证
rv = C_VerifyInit(hSession, &mechanism, hPublicKey);
if (rv != CKR_OK) {
    printf("验证初始化失败\n");
    return rv;
}

// 执行验证
rv = C_Verify(hSession, data, sizeof(data)-1, signature, signatureLen);
if (rv == CKR_OK) {
    printf("签名验证成功:签名有效\n");
} else if (rv == CKR_SIGNATURE_INVALID) {
    printf("签名验证失败:签名无效\n");
} else {
    printf("签名验证错误:%08x\n", rv);
}

完整的签名验证流程示例

CK_RV perform_sign_verify_demo(CK_SESSION_HANDLE hSession) {
    CK_RV rv;
    CK_MECHANISM mechanism = {CKM_RSA_PKCS, NULL_PTR, 0};
    CK_BYTE message[] = "Test message for signing";
    CK_BYTE signature[256];
    CK_ULONG signatureLen = 256;
    
    // 1. 查找私钥
    CK_OBJECT_CLASS privKeyClass = CKO_PRIVATE_KEY;
    CK_KEY_TYPE rsaKeyType = CKK_RSA;
    CK_ATTRIBUTE findTemplate[] = {
        {CKA_CLASS, &privKeyClass, sizeof(privKeyClass)},
        {CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)},
    };
    
    CK_OBJECT_HANDLE hPrivateKey, hPublicKey;
    CK_ULONG ulCount;
    
    rv = C_FindObjectsInit(hSession, findTemplate, 2);
    rv = C_FindObjects(hSession, &hPrivateKey, 1, &ulCount);
    rv = C_FindObjectsFinal(hSession);
    
    if (ulCount == 0) {
        printf("未找到私钥\n");
        return CKR_KEY_HANDLE_INVALID;
    }
    
    // 2. 初始化签名
    rv = C_SignInit(hSession, &mechanism, hPrivateKey);
    if (rv != CKR_OK) {
        printf("签名初始化失败:%08x\n", rv);
        return rv;
    }
    
    // 3. 执行签名
    rv = C_Sign(hSession, message, sizeof(message)-1, signature, &signatureLen);
    if (rv != CKR_OK) {
        printf("签名失败:%08x\n", rv);
        return rv;
    }
    
    printf("签名成功,长度:%lu字节\n", signatureLen);
    
    // 4. 查找公钥
    CK_OBJECT_CLASS pubKeyClass = CKO_PUBLIC_KEY;
    CK_ATTRIBUTE pubFindTemplate[] = {
        {CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass)},
        {CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)},
    };
    
    rv = C_FindObjectsInit(hSession, pubFindTemplate, 2);
    rv = C_FindObjects(hSession, &hPublicKey, 1, &ulCount);
    rv = C_FindObjectsFinal(hSession);
    
    if (ulCount == 0) {
        printf("未找到公钥\n");
        return CKR_KEY_HANDLE_INVALID;
    }
    
    // 5. 初始化验证
    rv = C_VerifyInit(hSession, &mechanism, hPublicKey);
    if (rv != CKR_OK) {
        printf("验证初始化失败:%08x\n", rv);
        return rv;
    }
    
    // 6. 执行验证
    rv = C_Verify(hSession, message, sizeof(message)-1, signature, signatureLen);
    if (rv == CKR_OK) {
        printf("验证成功:签名有效\n");
    } else if (rv == CKR_SIGNATURE_INVALID) {
        printf("验证失败:签名无效\n");
    } else {
        printf("验证错误:%08x\n", rv);
    }
    
    return rv;
}

消息签名模式(PKCS#11 v3.0新增)

PKCS#11 v3.0新增了“消息签名“模式(规范第5.14节):

| 函数 | 描述 | |:—|:—|:—| | C_MessageSignInit | 初始化消息签名 | | C_SignMessage | 一次性消息签名 | | C_SignMessageBegin | 开始消息签名 | | C_SignMessageNext | 添加数据并可选签名 | | C_MessageSignFinal | 完成消息签名 |

消息签名模式与传统签名的区别:

  • 传统签名:先Init,再Sign,需要两个函数
  • 消息签名:SignMessage一步完成,更简洁
/* 传统签名 */
rv = C_SignInit(hSession, &mechanism, hKey);
rv = C_Sign(hSession, data, len, sig, &sigLen);

/* 消息签名(v3.0) */
CK_VOID_PTR pParams = NULL_PTR;
rv = C_MessageSignInit(hSession, &mechanism, hKey);
rv = C_SignMessage(hSession, pParams, paramLen, data, len, sig, &sigLen);

本篇小结

签名验证是密码操作的核心功能:

签名函数

  • C_SignInit:初始化签名(指定私钥和机制)
  • C_Sign:单步签名
  • C_SignUpdate + C_SignFinal:多步签名

验证函数

  • C_VerifyInit:初始化验证(指定公钥和机制)
  • C_Verify:单步验证
  • C_VerifyUpdate + C_VerifyFinal:多步验证

签名机制

  • RSA:CKM_RSA_PKCS(PKCS#1 v1.5)、CKM_RSA_PKCS_PSS(PSS)
  • ECDSA:CKM_ECDSA、CKM_ECDSA_SHA256

返回值

  • CKR_OK:签名有效
  • CKR_SIGNATURE_INVALID:签名无效

消息签名模式(v3.0)

  • C_SignMessage:一步完成签名

下一节,我们将学习哈希与MAC函数——数据的“指纹与印章“。

【下集预告】

  • PKCS#11支持哪些哈希算法?

  • 如何进行多步哈希运算?

  • HMAC如何使用C_Sign实现?

  • 双操作函数(DigestEncrypt等)有何用途?

下一节,哈希与MAC函数详解。