使用GmSSL v3.1.1实现SM2证书认证

1、首先使用gmssl命令生成根证书、客户端公私钥,然后使用根证书签发客户端证书;

2、然后编写代码完成认证功能,使用根证书验证客户端证书是否由自己签发,然后使用客户端证书验证客户端私钥对随机数的签名是否正确。

第一部分生成根证书和客户端证书的命令:

bash 复制代码
# 生成私钥,用于生成根证书
# gmssl 3.1.1要求必须使用密码加密私钥
gmssl sm2keygen -pass 123456 -out root.key

# 生成自签名根证书
gmssl certgen \
  -C CN \
  -ST Beijing \
  -L Beijing \
  -O MyCA \
  -OU RootCA \
  -CN RootCA \
  -days 3650 \
  -key root.key \
  -pass 123456 \
  -key_usage keyCertSign \
  -out root.crt
 
# 生成客户端公私钥
gmssl sm2keygen -pass 123456 -out client.key

# 生成客户端证书请求(CSR)
gmssl reqgen \
  -C CN \
  -ST Beijing \
  -L Beijing \
  -O MyClient \
  -OU Client \
  -CN client.example.com \
  -key client.key \
  -pass 123456 \
  -out client.csr
  
# 签发客户端证书
gmssl reqsign \
  -in client.csr \
  -days 3650 \
  -key_usage digitalSignature \
  -cacert root.crt \
  -key root.key \
  -pass 123456 \
  -out client.crt

第二部分进行客户端证书认证和签名验签的代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmssl/x509.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/pem.h>
#include <gmssl/error.h>
#include <gmssl/base64.h>

#define ROOT_CERT_FILE   "root.crt"
#define CLIENT_CERT_FILE "client.crt"
#define CLIENT_KEY_FILE  "client.key"
#define ROOT_KEY_FILE    "root.key"
#define DATA_TO_SIGN     "This is the data to sign."

// 读取加密的SM2私钥(PKCS#8 PEM),解密并导入SM2_KEY
int load_encrypted_sm2_private_key(const char *filename, const char *password, SM2_KEY *key) {
    FILE *fp = fopen(filename, "r");
    if (!fp) {
        perror(filename);
        return -1;
    }
    if (sm2_private_key_info_decrypt_from_pem(key, password, fp) != 1) {
        fprintf(stderr, "Failed to decrypt/load private key: %s\n", filename);
        fclose(fp);
        return -1;
    }
    fclose(fp);
    return 0;
}

int main(void) {
    uint8_t root_cert[4096], client_cert[4096];
    size_t root_cert_len = 0, client_cert_len = 0;
    SM2_KEY client_key, client_pubkey;
    uint8_t dgst[32];
    uint8_t sig[SM2_MAX_SIGNATURE_SIZE];
    size_t siglen = 0;
    int ret = 1;

    // 1. 读取根证书
    FILE *fp = fopen(ROOT_CERT_FILE, "r");
    if (!fp) {
        perror("open root.crt");
        return 1;
    }
    if (x509_cert_from_pem(root_cert, &root_cert_len, sizeof(root_cert), fp) != 1) {
        fprintf(stderr, "Failed to load root certificate\n");
        fclose(fp);
        return 1;
    }
    fclose(fp);

    // 2. 读取客户端证书
    fp = fopen(CLIENT_CERT_FILE, "r");
    if (!fp) {
        perror("open client.crt");
        return 1;
    }
    if (x509_cert_from_pem(client_cert, &client_cert_len, sizeof(client_cert), fp) != 1) {
        fprintf(stderr, "Failed to load client certificate\n");
        fclose(fp);
        return 1;
    }
    fclose(fp);

    // 3. 验证客户端证书
    if (x509_cert_verify_by_ca_cert(client_cert, client_cert_len, root_cert,
                        root_cert_len, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1) {
        fprintf(stderr, "Client certificate verify failed\n");
        goto end;
    }
    printf("Client certificate verified successfully.\n");

    // 4. 读取加密的客户端私钥(需要口令)
    char client_pass[7] = {0x31,0x32,0x33,0x34,0x35,0x36,0x00};
    if (load_encrypted_sm2_private_key(CLIENT_KEY_FILE, client_pass, &client_key) != 0) {
        fprintf(stderr, "Failed to load client private key\n");
        goto end;
    }

    // 5. 计算数据摘要
    const uint8_t *data = (const uint8_t *)DATA_TO_SIGN;
    size_t data_len = strlen(DATA_TO_SIGN);
    sm3_digest(data, data_len, dgst);

    // 6. 签名
    if (sm2_sign(&client_key, dgst, sig, &siglen) != 1) {
        fprintf(stderr, "SM2 sign failed\n");
        goto end;
    }
    printf("Data signed successfully.\nSignature: ");
    for (size_t i = 0; i < siglen; i++) printf("%02x", sig[i]);
    printf("\n");

    // 7. 从证书中提取公钥
    if (x509_cert_get_subject_public_key(client_cert, client_cert_len, &client_pubkey) != 1) {
        fprintf(stderr, "Failed to extract public key from client certificate\n");
        goto end;
    }

    // 8. 验签
    if (sm2_verify(&client_pubkey, dgst, sig, siglen) != 1) {
        fprintf(stderr, "Signature verification failed\n");
        goto end;
    }
    printf("Signature verified successfully.\n");
    ret = 0;

end:
    //sm2_key_cleanup(&client_key);
    //sm2_key_cleanup(&client_pubkey);
    return ret;
}

在centos7平台使用GmSSL V3.1.1亲测可行。

相关推荐
凡泰极客科技7 分钟前
从数据质检到运行审计:金融AI智能体如何安全且合规落地?
人工智能·安全·金融
Token架构师14 分钟前
给 Claude Code 套一层「进程外」策略:OpenShell 落地与审计日志分析
人工智能·安全·架构
邵奈一20 分钟前
当沉香之乡遇上 Seed-2.1-pro:我用 AI 给村子做了一个数字门面
算法·架构
HA-ming22 分钟前
换个角度看PCSwitch:一套呼叫系统的成本结构和安全设计
安全·语音识别·呼叫中心·智能呼叫系统·智能外呼·ai呼叫系统
海天一色y29 分钟前
图像分割全解析:从经典算法到深度学习的原理与实战(Python + MATLAB)
python·深度学习·算法
垆边人似月.1 小时前
华为算法题:最长连续递增子序列的变
数据结构·算法·leetcode
YOLO数据集集合1 小时前
机器学习二分类预测算法组合工具:12种算法、113种组合的批量建模方案8031期
算法·机器学习
Xudde.1 小时前
Me and My Girlfriend 靶机渗透测试(Vulnhub Writeup)
笔记·学习·安全·web安全
段一凡-华北理工大学1 小时前
大模型与智能体在工业的应用~系列文章10:可靠性篇:大模型的“幻觉“与工业安全,如何让 AI 可信
大数据·人工智能·安全·大模型幻觉·工业智能化·高炉炼铁智能化·ai可信度
longlongzihan1 小时前
LeetCode 56合并区间:排序与贪心
c++·算法·leetcode