使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签:

        操作系统:centos7.9

        openssl版本:v1.1.1u

#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <string.h>

// 全局变量,存储公私钥对的 PEM 格式数据
char *publicKeyPEM = NULL;
char *privateKeyPEM = NULL;

// 辅助函数,用于从 EC_KEY 转换为 EVP_PKEY
EVP_PKEY *EC_KEY_to_EVP_PKEY(EC_KEY *ec_key) {
    EVP_PKEY *pkey = EVP_PKEY_new();
    if (!pkey) {
        fprintf(stderr, "Error: Failed to create EVP_PKEY.\n");
        return NULL;
    }

    if (!EVP_PKEY_set1_EC_KEY(pkey, ec_key)) {
        EVP_PKEY_free(pkey);
        fprintf(stderr, "Error: Failed to set EVP_PKEY with EC_KEY.\n");
        return NULL;
    }

    return pkey;
}

int SM2_sign(const char *sourcefilename, const char *sigfilename) {
    int ret = 0;

    // 从全局变量中获取私钥
    BIO *bio_mem = BIO_new(BIO_s_mem());
    BIO_write(bio_mem, privateKeyPEM, strlen(privateKeyPEM));
    EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL);
    BIO_free_all(bio_mem);
    if (pkey == NULL) {
        fprintf(stderr, "Error: Unable to read private key from global variable.\n");
        return -1;
    }

    /* compute SM2 signature */
    EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2);

    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
    EVP_MD_CTX_set_pkey_ctx(mctx, ctx);

    ret = EVP_DigestSignInit(mctx, NULL, EVP_sm3(), NULL, pkey);

    // 打开源文件,计算文件的哈希值
    FILE *fp2 = fopen(sourcefilename, "rb");
    if (fp2 == NULL) {
        fprintf(stderr, "Error: Unable to open source file %s\n", sourcefilename);
        EVP_PKEY_free(pkey);
        return -1;
    }
    int n = 0;
    unsigned char buffer[1024];
    while ((n = fread(buffer, 1, sizeof(buffer), fp2)) > 0) {
        EVP_DigestSignUpdate(mctx, buffer, n);
    }
    fclose(fp2);

    // 计算文件的签名值
    size_t sig_len;
    EVP_DigestSignFinal(mctx, NULL, &sig_len);
    unsigned char *sig = (unsigned char *)malloc(sig_len);
    EVP_DigestSignFinal(mctx, sig, &sig_len);

    // 打印签名值长度和签名值
    printf("签名值长度:%d\n", sig_len);
    printf("签名值:");
    for (int i = 0; i < sig_len; i++) {
        printf("%02x", sig[i]);
    }
    printf("\n");

    // 将文件的签名值和长度写入到输出文件
    FILE *fp3 = fopen(sigfilename, "wb");
    if (fp3 == NULL) {
        fprintf(stderr, "Error: Unable to open signature file %s\n", sigfilename);
        free(sig);
        EVP_MD_CTX_free(mctx);
        EVP_PKEY_CTX_free(ctx);
        EVP_PKEY_free(pkey);
        return -1;
    }
    fwrite(&sig_len, sizeof(sig_len), 1, fp3);
    fwrite(sig, 1, sig_len, fp3);
    fflush(fp3);
    fclose(fp3);

    // 释放资源
    free(sig);
    EVP_MD_CTX_free(mctx);
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(pkey);
    return 1;
}

int SM2_verify(const char *sourcefile, const char *sigfilename) {
    // 从全局变量中获取公钥
    BIO *bio_mem = BIO_new(BIO_s_mem());
    BIO_write(bio_mem, publicKeyPEM, strlen(publicKeyPEM));
    EVP_PKEY *pkey = NULL;
    EC_KEY *ec_key = PEM_read_bio_EC_PUBKEY(bio_mem, NULL, NULL, NULL);
    BIO_free_all(bio_mem);
    if (ec_key == NULL) {
        fprintf(stderr, "Error: Unable to read public key from global variable.\n");
        return -1;
    }
    
    pkey = EC_KEY_to_EVP_PKEY(ec_key);

    // 打开存储签名值的文件,读出签名值
    FILE *fp2 = fopen(sigfilename, "rb");
    if (fp2 == NULL) {
        fprintf(stderr, "Error: Unable to open signature file %s\n", sigfilename);
        EVP_PKEY_free(pkey);
        return -1;
    }
    size_t sig_len;
    fread(&sig_len, sizeof(sig_len), 1, fp2);
    unsigned char *sig = (unsigned char *)malloc(sig_len);
    fread(sig, 1, sig_len, fp2);
    fclose(fp2);

    /* verify SM2 signature */
    EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2);

    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
    EVP_MD_CTX_set_pkey_ctx(mctx, ctx);

    EVP_DigestVerifyInit(mctx, NULL, EVP_sm3(), NULL, pkey);

    // 打开源文件,计算哈希值
    FILE *fp3 = fopen(sourcefile, "rb");
    if (fp3 == NULL) {
        fprintf(stderr, "Error: Unable to open source file %s\n", sourcefile);
        free(sig);
        EVP_MD_CTX_free(mctx);
        EVP_PKEY_CTX_free(ctx);
        EVP_PKEY_free(pkey);
        return -1;
    }
    int n = 0;
    unsigned char buffer[1024];
    while ((n = fread(buffer, 1, sizeof(buffer), fp3)) > 0) {
        EVP_DigestVerifyUpdate(mctx, buffer, n);
    }
    fclose(fp3);

    // 计算签名值,并和源签名值比对,验签
    int ret = 0;
    if ((EVP_DigestVerifyFinal(mctx, sig, sig_len)) != 1) {
        printf("Verify SM2 signature failed!\n");
        ret = 0;
    } else {
        printf("Verify SM2 signature succeeded!\n");
        ret = 1;
    }
    fflush(stdout);

    // 释放资源
    free(sig);
    EVP_MD_CTX_free(mctx);
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(pkey);
    return ret;
}

// 辅助函数,用于从 EVP_PKEY 中提取 EC_KEY
EC_KEY *EVP_PKEY_to_EC_KEY(EVP_PKEY *pkey) {
    if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
        fprintf(stderr, "Error: The key is not an EC key.\n");
        return NULL;
    }
    return EVP_PKEY_get1_EC_KEY(pkey);
}

int main(int argc, const char *argv[]) {
    // 生成 SM2 密钥对
    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
    EVP_PKEY_paramgen_init(pctx);
    EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_sm2);

    EVP_PKEY *pkey = EVP_PKEY_new();
    EVP_PKEY_keygen_init(pctx);
    EVP_PKEY_keygen(pctx, &pkey);

    // 将公私钥对保存到 PEM 格式字符串
    BIO *bio_mem = BIO_new(BIO_s_mem());
    PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);

    // 获取 PEM 格式字符串
    char *pem_data;
    int len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
    privateKeyPEM = (char *)malloc(len + 1);
    memcpy(privateKeyPEM, pem_data, len); // 将数据复制到 privateKeyPEM
    privateKeyPEM[len] = '\0';
    printf("privateKeyPEM:\n%s\n", privateKeyPEM);

    // 从 EVP_PKEY 提取出 EC_KEY
    EC_KEY *ec_key = EVP_PKEY_to_EC_KEY(pkey);
    BIO_reset(bio_mem);
    PEM_write_bio_EC_PUBKEY(bio_mem, ec_key);
    //PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);
    

    // 获取 PEM 格式字符串
    len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
    publicKeyPEM = (char *)malloc(len + 1);
    memcpy(publicKeyPEM, pem_data, len); // 将数据复制到 publicKeyPEM
    publicKeyPEM[len] = '\0';
    printf("publicKeyPEM:\n%s\n", publicKeyPEM);
    BIO_free_all(bio_mem);

    EVP_PKEY_free(pkey);
    EVP_PKEY_CTX_free(pctx);

    // 使用全局变量进行签名和验签
    SM2_sign(argv[1], argv[2]);
    SM2_verify(argv[1], argv[2]);

    // 释放全局变量
    free(publicKeyPEM);
    free(privateKeyPEM);

    return 0;
}

编译参数(我的openssl是自己编译的,且存放在当前目录下):

gcc -std=gnu99 -g -O0 sm2-pem.c -o sm2-pem -L./lib -lcrypto -I./include -Wl,-rpath=./lib

验证(sig.in为待签名文件, sig.out为存放签名值文件):

./sm2-pem sig.in sig.out
privateKeyPEM:
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQggaj9z7vLVE71ab3Q
ax67pps1j4v1eZKJbtRD/OIp0rihRANCAAS8q/3Al0aDdMB3Rl81a/7+MeYyWwYw
L2JFdNYPMljd8jkANchRPc1n5hg8uE7kBMD0PRZO/vU9CPAWRrlbvDn7
-----END PRIVATE KEY-----

publicKeyPEM:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEvKv9wJdGg3TAd0ZfNWv+/jHmMlsG
MC9iRXTWDzJY3fI5ADXIUT3NZ+YYPLhO5ATA9D0WTv71PQjwFka5W7w5+w==
-----END PUBLIC KEY-----

签名值长度:71
签名值:3045022100bb13af9eb17f61c3568cef795c618ba052256b0d2e56e15a1143a688bacbbd7702203564eb64157d2403172474cc74473c0cee64436bcbf1fee77b97138b5229734d
Verify SM2 signature succeeded!

GitHub 加速计划 / ope / openssl
20
1
下载
传输层安全性/安全套接层及其加密库
最近提交(Master分支:3 个月前 )
4f37e543 Recent test additions have increased the number of jobs spawned by the interop runner test which exceeds the maximum allowed. This occurs because the matrix expands to: 7 server elements 6 client elements 7 tests 2 test steps (client interop and server interop Because of how github ci does matrix expansion, this results in 2 * 7 * 7 * 6 = 588 But most of those are invalid because each of the 2 steps only considers either the client or server elements, and so get rerun multiple times Alter the steps to be individual jobs, each with their own reduced matrix to only run each relevant test once, limiting our job count to at most 49 jobs. Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Saša Nedvědický <sashan@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26275) 5 天前
c93f4a1e This makes `ikmlen` have a length of at least `Nsk`. Closes #26213 Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26254) 5 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐