0


使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签

  1. 使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签:
  2. 操作系统:centos7.9
  3. 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. pkey = EC_KEY_to_EVP_PKEY(ec_key);
  99. // 打开存储签名值的文件,读出签名值
  100. FILE *fp2 = fopen(sigfilename, "rb");
  101. if (fp2 == NULL) {
  102. fprintf(stderr, "Error: Unable to open signature file %s\n", sigfilename);
  103. EVP_PKEY_free(pkey);
  104. return -1;
  105. }
  106. size_t sig_len;
  107. fread(&sig_len, sizeof(sig_len), 1, fp2);
  108. unsigned char *sig = (unsigned char *)malloc(sig_len);
  109. fread(sig, 1, sig_len, fp2);
  110. fclose(fp2);
  111. /* verify SM2 signature */
  112. EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2);
  113. EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
  114. EVP_MD_CTX *mctx = EVP_MD_CTX_new();
  115. EVP_MD_CTX_set_pkey_ctx(mctx, ctx);
  116. EVP_DigestVerifyInit(mctx, NULL, EVP_sm3(), NULL, pkey);
  117. // 打开源文件,计算哈希值
  118. FILE *fp3 = fopen(sourcefile, "rb");
  119. if (fp3 == NULL) {
  120. fprintf(stderr, "Error: Unable to open source file %s\n", sourcefile);
  121. free(sig);
  122. EVP_MD_CTX_free(mctx);
  123. EVP_PKEY_CTX_free(ctx);
  124. EVP_PKEY_free(pkey);
  125. return -1;
  126. }
  127. int n = 0;
  128. unsigned char buffer[1024];
  129. while ((n = fread(buffer, 1, sizeof(buffer), fp3)) > 0) {
  130. EVP_DigestVerifyUpdate(mctx, buffer, n);
  131. }
  132. fclose(fp3);
  133. // 计算签名值,并和源签名值比对,验签
  134. int ret = 0;
  135. if ((EVP_DigestVerifyFinal(mctx, sig, sig_len)) != 1) {
  136. printf("Verify SM2 signature failed!\n");
  137. ret = 0;
  138. } else {
  139. printf("Verify SM2 signature succeeded!\n");
  140. ret = 1;
  141. }
  142. fflush(stdout);
  143. // 释放资源
  144. free(sig);
  145. EVP_MD_CTX_free(mctx);
  146. EVP_PKEY_CTX_free(ctx);
  147. EVP_PKEY_free(pkey);
  148. return ret;
  149. }
  150. // 辅助函数,用于从 EVP_PKEY 中提取 EC_KEY
  151. EC_KEY *EVP_PKEY_to_EC_KEY(EVP_PKEY *pkey) {
  152. if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
  153. fprintf(stderr, "Error: The key is not an EC key.\n");
  154. return NULL;
  155. }
  156. return EVP_PKEY_get1_EC_KEY(pkey);
  157. }
  158. int main(int argc, const char *argv[]) {
  159. // 生成 SM2 密钥对
  160. EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
  161. EVP_PKEY_paramgen_init(pctx);
  162. EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_sm2);
  163. EVP_PKEY *pkey = EVP_PKEY_new();
  164. EVP_PKEY_keygen_init(pctx);
  165. EVP_PKEY_keygen(pctx, &pkey);
  166. // 将公私钥对保存到 PEM 格式字符串
  167. BIO *bio_mem = BIO_new(BIO_s_mem());
  168. PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);
  169. // 获取 PEM 格式字符串
  170. char *pem_data;
  171. int len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
  172. privateKeyPEM = (char *)malloc(len + 1);
  173. memcpy(privateKeyPEM, pem_data, len); // 将数据复制到 privateKeyPEM
  174. privateKeyPEM[len] = '\0';
  175. printf("privateKeyPEM:\n%s\n", privateKeyPEM);
  176. // 从 EVP_PKEY 提取出 EC_KEY
  177. EC_KEY *ec_key = EVP_PKEY_to_EC_KEY(pkey);
  178. BIO_reset(bio_mem);
  179. PEM_write_bio_EC_PUBKEY(bio_mem, ec_key);
  180. //PEM_write_bio_PrivateKey(bio_mem, pkey, NULL, NULL, 0, NULL, NULL);
  181. // 获取 PEM 格式字符串
  182. len = BIO_get_mem_data(bio_mem, &pem_data); // 获取数据指针和长度
  183. publicKeyPEM = (char *)malloc(len + 1);
  184. memcpy(publicKeyPEM, pem_data, len); // 将数据复制到 publicKeyPEM
  185. publicKeyPEM[len] = '\0';
  186. printf("publicKeyPEM:\n%s\n", publicKeyPEM);
  187. BIO_free_all(bio_mem);
  188. EVP_PKEY_free(pkey);
  189. EVP_PKEY_CTX_free(pctx);
  190. // 使用全局变量进行签名和验签
  191. SM2_sign(argv[1], argv[2]);
  192. SM2_verify(argv[1], argv[2]);
  193. // 释放全局变量
  194. free(publicKeyPEM);
  195. free(privateKeyPEM);
  196. return 0;
  197. }

编译参数(我的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!
标签: 算法 ssl 密码学

本文转载自: https://blog.csdn.net/weixin_38700215/article/details/140740988
版权归原作者 weixin_38700215 所有, 如有侵权,请联系我们删除。

“使用OPENSSL实现SM2生成PEM格式公私钥对并用于签名验签”的评论:

还没有评论