使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签: 操作系统:centos7.9 openssl版本:v1.1.1u [code]#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; }[/code]编译参数(我的openssl是自己编译的,且存放在当前目录下): [code]gcc -std=gnu99 -g -O0 sm2-pem.c -o sm2-pem -L./lib -lcrypto -I./include -Wl,-rpath=./lib[/code]验证(sig.in为待签名文件, sig.out为存放签名值文件): [code]./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! [/code]免责声明:本内容来源于网络,如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |