利用OPENSSL实现SM2天生PEM格式公私钥对并用于署名验签

[复制链接]
发表于 2026-1-6 06:13:27 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
        利用OPENSSL实现SM2天生PEM格式公私钥对并用于署名验签:
        利用体系:centos7.9
        openssl版本:v1.1.1u
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <openssl/ec.h>
  4. #include <openssl/evp.h>
  5. #include <openssl/pem.h>
  6. #include <string.h>
  7. // 全局变量,存储公私钥对的 PEM 格式数据
  8. char *publicKeyPEM = NULL;
  9. char *privateKeyPEM = NULL;
  10. // 辅助函数,用于从 EC_KEY 转换为 EVP_PKEY
  11. EVP_PKEY *EC_KEY_to_EVP_PKEY(EC_KEY *ec_key) {
  12.     EVP_PKEY *pkey = EVP_PKEY_new();
  13.     if (!pkey) {
  14.         fprintf(stderr, "Error: Failed to create EVP_PKEY.\n");
  15.         return NULL;
  16.     }
  17.     if (!EVP_PKEY_set1_EC_KEY(pkey, ec_key)) {
  18.         EVP_PKEY_free(pkey);
  19.         fprintf(stderr, "Error: Failed to set EVP_PKEY with EC_KEY.\n");
  20.         return NULL;
  21.     }
  22.     return pkey;
  23. }
  24. int SM2_sign(const char *sourcefilename, const char *sigfilename) {
  25.     int ret = 0;
  26.     // 从全局变量中获取私钥
  27.     BIO *bio_mem = BIO_new(BIO_s_mem());
  28.     BIO_write(bio_mem, privateKeyPEM, strlen(privateKeyPEM));
  29.     EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL);
  30.     BIO_free_all(bio_mem);
  31.     if (pkey == NULL) {
  32.         fprintf(stderr, "Error: Unable to read private key from global variable.\n");
  33.         return -1;
  34.     }
  35.     /* compute SM2 signature */
  36.     EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2);
  37.     EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
  38.     EVP_MD_CTX *mctx = EVP_MD_CTX_new();
  39.     EVP_MD_CTX_set_pkey_ctx(mctx, ctx);
  40.     ret = EVP_DigestSignInit(mctx, NULL, EVP_sm3(), NULL, pkey);
  41.     // 打开源文件,计算文件的哈希值
  42.     FILE *fp2 = fopen(sourcefilename, "rb");
  43.     if (fp2 == NULL) {
  44.         fprintf(stderr, "Error: Unable to open source file %s\n", sourcefilename);
  45.         EVP_PKEY_free(pkey);
  46.         return -1;
  47.     }
  48.     int n = 0;
  49.     unsigned char buffer[1024];
  50.     while ((n = fread(buffer, 1, sizeof(buffer), fp2)) > 0) {
  51.         EVP_DigestSignUpdate(mctx, buffer, n);
  52.     }
  53.     fclose(fp2);
  54.     // 计算文件的签名值
  55.     size_t sig_len;
  56.     EVP_DigestSignFinal(mctx, NULL, &sig_len);
  57.     unsigned char *sig = (unsigned char *)malloc(sig_len);
  58.     EVP_DigestSignFinal(mctx, sig, &sig_len);
  59.     // 打印签名值长度和签名值
  60.     printf("签名值长度:%d\n", sig_len);
  61.     printf("签名值:");
  62.     for (int i = 0; i < sig_len; i++) {
  63.         printf("%02x", sig[i]);
  64.     }
  65.     printf("\n");
  66.     // 将文件的签名值和长度写入到输出文件
  67.     FILE *fp3 = fopen(sigfilename, "wb");
  68.     if (fp3 == NULL) {
  69.         fprintf(stderr, "Error: Unable to open signature file %s\n", sigfilename);
  70.         free(sig);
  71.         EVP_MD_CTX_free(mctx);
  72.         EVP_PKEY_CTX_free(ctx);
  73.         EVP_PKEY_free(pkey);
  74.         return -1;
  75.     }
  76.     fwrite(&sig_len, sizeof(sig_len), 1, fp3);
  77.     fwrite(sig, 1, sig_len, fp3);
  78.     fflush(fp3);
  79.     fclose(fp3);
  80.     // 释放资源
  81.     free(sig);
  82.     EVP_MD_CTX_free(mctx);
  83.     EVP_PKEY_CTX_free(ctx);
  84.     EVP_PKEY_free(pkey);
  85.     return 1;
  86. }
  87. int SM2_verify(const char *sourcefile, const char *sigfilename) {
  88.     // 从全局变量中获取公钥
  89.     BIO *bio_mem = BIO_new(BIO_s_mem());
  90.     BIO_write(bio_mem, publicKeyPEM, strlen(publicKeyPEM));
  91.     EVP_PKEY *pkey = NULL;
  92.     EC_KEY *ec_key = PEM_read_bio_EC_PUBKEY(bio_mem, NULL, NULL, NULL);
  93.     BIO_free_all(bio_mem);
  94.     if (ec_key == NULL) {
  95.         fprintf(stderr, "Error: Unable to read public key from global variable.\n");
  96.         return -1;
  97.     }
  98.    
  99.     pkey = EC_KEY_to_EVP_PKEY(ec_key);
  100.     // 打开存储签名值的文件,读出签名值
  101.     FILE *fp2 = fopen(sigfilename, "rb");
  102.     if (fp2 == NULL) {
  103.         fprintf(stderr, "Error: Unable to open signature file %s\n", sigfilename);
  104.         EVP_PKEY_free(pkey);
  105.         return -1;
  106.     }
  107.     size_t sig_len;
  108.     fread(&sig_len, sizeof(sig_len), 1, fp2);
  109.     unsigned char *sig = (unsigned char *)malloc(sig_len);
  110.     fread(sig, 1, sig_len, fp2);
  111.     fclose(fp2);
  112.     /* verify SM2 signature */
  113.     EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2);
  114.     EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
  115.     EVP_MD_CTX *mctx = EVP_MD_CTX_new();
  116.     EVP_MD_CTX_set_pkey_ctx(mctx, ctx);
  117.     EVP_DigestVerifyInit(mctx, NULL, EVP_sm3(), NULL, pkey);
  118.     // 打开源文件,计算哈希值
  119.     FILE *fp3 = fopen(sourcefile, "rb");
  120.     if (fp3 == NULL) {
  121.         fprintf(stderr, "Error: Unable to open source file %s\n", sourcefile);
  122.         free(sig);
  123.         EVP_MD_CTX_free(mctx);
  124.         EVP_PKEY_CTX_free(ctx);
  125.         EVP_PKEY_free(pkey);
  126.         return -1;
  127.     }
  128.     int n = 0;
  129.     unsigned char buffer[1024];
  130.     while ((n = fread(buffer, 1, sizeof(buffer), fp3)) > 0) {
  131.         EVP_DigestVerifyUpdate(mctx, buffer, n);
  132.     }
  133.     fclose(fp3);
  134.     // 计算签名值,并和源签名值比对,验签
  135.     int ret = 0;
  136.     if ((EVP_DigestVerifyFinal(mctx, sig, sig_len)) != 1) {
  137.         printf("Verify SM2 signature failed!\n");
  138.         ret = 0;
  139.     } else {
  140.         printf("Verify SM2 signature succeeded!\n");
  141.         ret = 1;
  142.     }
  143.     fflush(stdout);
  144.     // 释放资源
  145.     free(sig);
  146.     EVP_MD_CTX_free(mctx);
  147.     EVP_PKEY_CTX_free(ctx);
  148.     EVP_PKEY_free(pkey);
  149.     return ret;
  150. }
  151. // 辅助函数,用于从 EVP_PKEY 中提取 EC_KEY
  152. EC_KEY *EVP_PKEY_to_EC_KEY(EVP_PKEY *pkey) {
  153.     if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
  154.         fprintf(stderr, "Error: The key is not an EC key.\n");
  155.         return NULL;
  156.     }
  157.     return EVP_PKEY_get1_EC_KEY(pkey);
  158. }
  159. int main(int argc, const char *argv[]) {
  160.     // 生成 SM2 密钥对
  161.     EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
  162.     EVP_PKEY_paramgen_init(pctx);
  163.     EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_sm2);
  164.     EVP_PKEY *pkey = EVP_PKEY_new();
  165.     EVP_PKEY_keygen_init(pctx);
  166.     EVP_PKEY_keygen(pctx, &pkey);
  167.     // 将公私钥对保存到 PEM 格式字符串
  168.     BIO *bio_mem = BIO_new(BIO_s_mem());
  169.     PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);
  170.     // 获取 PEM 格式字符串
  171.     char *pem_data;
  172.     int len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
  173.     privateKeyPEM = (char *)malloc(len + 1);
  174.     memcpy(privateKeyPEM, pem_data, len); // 将数据复制到 privateKeyPEM
  175.     privateKeyPEM[len] = '\0';
  176.     printf("privateKeyPEM:\n%s\n", privateKeyPEM);
  177.     // 从 EVP_PKEY 提取出 EC_KEY
  178.     EC_KEY *ec_key = EVP_PKEY_to_EC_KEY(pkey);
  179.     BIO_reset(bio_mem);
  180.     PEM_write_bio_EC_PUBKEY(bio_mem, ec_key);
  181.     //PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);
  182.    
  183.     // 获取 PEM 格式字符串
  184.     len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
  185.     publicKeyPEM = (char *)malloc(len + 1);
  186.     memcpy(publicKeyPEM, pem_data, len); // 将数据复制到 publicKeyPEM
  187.     publicKeyPEM[len] = '\0';
  188.     printf("publicKeyPEM:\n%s\n", publicKeyPEM);
  189.     BIO_free_all(bio_mem);
  190.     EVP_PKEY_free(pkey);
  191.     EVP_PKEY_CTX_free(pctx);
  192.     // 使用全局变量进行签名和验签
  193.     SM2_sign(argv[1], argv[2]);
  194.     SM2_verify(argv[1], argv[2]);
  195.     // 释放全局变量
  196.     free(publicKeyPEM);
  197.     free(privateKeyPEM);
  198.     return 0;
  199. }
复制代码
编译参数(我的openssl是本身编译的,且存放在当前目次下):
  1. gcc -std=gnu99 -g -O0 sm2-pem.c -o sm2-pem -L./lib -lcrypto -I./include -Wl,-rpath=./lib
复制代码
验证(sig.in为待署名文件, sig.out为存放署名值文件):
  1. ./sm2-pem sig.in sig.out
  2. privateKeyPEM:
  3. -----BEGIN PRIVATE KEY-----
  4. MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQggaj9z7vLVE71ab3Q
  5. ax67pps1j4v1eZKJbtRD/OIp0rihRANCAAS8q/3Al0aDdMB3Rl81a/7+MeYyWwYw
  6. L2JFdNYPMljd8jkANchRPc1n5hg8uE7kBMD0PRZO/vU9CPAWRrlbvDn7
  7. -----END PRIVATE KEY-----
  8. publicKeyPEM:
  9. -----BEGIN PUBLIC KEY-----
  10. MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEvKv9wJdGg3TAd0ZfNWv+/jHmMlsG
  11. MC9iRXTWDzJY3fI5ADXIUT3NZ+YYPLhO5ATA9D0WTv71PQjwFka5W7w5+w==
  12. -----END PUBLIC KEY-----
  13. 签名值长度:71
  14. 签名值:3045022100bb13af9eb17f61c3568cef795c618ba052256b0d2e56e15a1143a688bacbbd7702203564eb64157d2403172474cc74473c0cee64436bcbf1fee77b97138b5229734d
  15. Verify SM2 signature succeeded!
复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表