使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签
openssl
传输层安全性/安全套接层及其加密库
项目地址:https://gitcode.com/gh_mirrors/ope/openssl
免费下载资源
·
使用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 天前
更多推荐
已为社区贡献1条内容
所有评论(0)