马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
利用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!
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |