0


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

    使用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!
标签: 算法 ssl 密码学

本文转载自: https://blog.csdn.net/weixin_38700215/article/details/140740988
版权归原作者 weixin_38700215 所有, 如有侵权,请联系我们删除。

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

还没有评论