使用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!
本文转载自: https://blog.csdn.net/weixin_38700215/article/details/140740988
版权归原作者 weixin_38700215 所有, 如有侵权,请联系我们删除。
版权归原作者 weixin_38700215 所有, 如有侵权,请联系我们删除。