当前位置:   article > 正文

基于 gmssl实现的sm2加密(C++)_gmssl sm2

gmssl sm2

项目中需要用到sm2加密,在网上搜索了一下相关的库,发现只有openssl和gmssl这两个库可以用,于是基于gmssl库做了封装,gmssl的版本是:GmSSL 2.5.4 - OpenSSL 1.1.0d  19 Jun 2019

搞这个库的确要费不少功夫,现在分享出来给需要的人。目前我是用在linux环境中,因此编译成linux动态库,并且屏蔽相关库的头文件和符号,只暴露sm2加解密相关的接口符号,gmssl库通过静态库的方式引用。

关于sm2加密有几个比较重要的参数,第一个是椭圆曲线参数,第二个是密文编码方式,第三个是哈希算法,目前我们用的都是固定的参数,所以封装的时候没有提供参数选择这些功能,需要的可以自行扩展。下面的sm2加密相关参数为:使用默认的椭圆曲线参数(sm2p256v1),ASN.1/DER编码方式(C1|C3|C2编码方式) ,哈希(杂凑)算法使用sm3

一些初学者可能还不大懂怎么引用外部库,我把我自己的工程上传到github上了,可以作为参考,gmssl库我已经编译好,生成的是静态库,但是gmssl库的编译其实还是应该用自己的机器去编译的,因为自己拿我编译好的静态库去用的话,不同的linux版本有可能不兼容。gmssl库文件我也放在该工程下了,可以不用去官网找,文件为:GmSSL-master.zip。 github地址:qingfeng1992/gmutil: 基于gmssl加密库封装sm2加解密方法 (github.com)

可以直接使用git clone git@github.com:qingfeng1992/gmutil.git 拉取代码

下载下来后请先看README.txt后再操作

gmutil.h头文件:

  1. #ifndef __GM_UTIL_H__
  2. #define __GM_UTIL_H__
  3. #include <string>
  4. using namespace std;
  5. #ifdef _WIN32
  6. #define UNIX_EXPORT
  7. #else
  8. #define UNIX_EXPORT __attribute__((visibility("default")))
  9. #endif
  10. // namespace GM
  11. //{
  12. // 错误码
  13. enum EGMErrorCode
  14. {
  15. GM_UTIL_CODE_OK = 0,
  16. GM_UTIL_CODE_CREATE_EV_KEY_FAILED, // 密钥解析失败
  17. GM_UTIL_CODE_SM2_ENCRYPT_FAILED, // SM2加密失败
  18. GM_UTIL_CODE_SM2_DECRYPT_FAILED, // SM2解密失败
  19. GM_UTIL_CODE_NOT_SM2P256V1, // 不是默认的sm2p256v1椭圆曲线参数
  20. GM_UTIL_CODE_INIT_BIO_FAILED, // 初始化BIO失败
  21. GM_UTIL_CODE_CIPHER_TEXT_TO_BIO_FAILED, // 加密数据存储到BIO失败
  22. GM_UTIL_CODE_BIO_DATA_TO_MEM_FAILED, // BIO数据转存到缓冲区失败
  23. GM_UTIL_CODE_BIO_DATA_TO_CIPHER_TEXT_FAILED, // BIO数据转成Ciphertext结构失败
  24. };
  25. extern "C"
  26. {
  27. // 从文件中读入公钥/私钥数据到string中,失败返回空字符串
  28. UNIX_EXPORT string GmReadKeyFromFile(string strFileName);
  29. /**
  30. * @brief sm2加密,使用默认的椭圆曲线参数(NID_sm2p256v1),ASN.1/DER编码方式(C1|C3|C2编码方式) ,哈希(杂凑)算法使用sm3
  31. * @param strPubKey 公钥数据
  32. * @param strIn 需要加密的数据
  33. * @param strCiphertext 密文,加密后的密文不是可见字符
  34. * @return 返回GM_UTIL_ERR_OK表示加密成功,否则失败,具体见EGMErrorCode定义
  35. */
  36. UNIX_EXPORT int GmSm2Encrypt(string strPubKey, const string &strIn, string &strCiphertext);
  37. /**
  38. * @brief sm2解密,使用默认的椭圆曲线参数(NID_sm2p256v1),ASN.1/DER编码方式(C1|C3|C2编码方式),哈希(杂凑)算法使用sm3
  39. * @param strPubKeyFile 私钥数据
  40. * @param strCiphertext 需要解密的数据(不是可见字符)
  41. * @param strOut 解密后的明文
  42. * @return 返回GM_UTIL_ERR_OK表示解密成功,否则失败,具体见EGMErrorCode定义
  43. */
  44. UNIX_EXPORT int GmSm2Decrypt(string strPriKey, const string &strCiphertext, string &strOut);
  45. // 将二进制数据转换成十六进制字符串
  46. UNIX_EXPORT string GmByte2HexStr(const string &data, bool bLowerCase = true);
  47. // 将十六进制字符串转换成二进制
  48. UNIX_EXPORT string GmHexStr2Byte(const string& hex, bool bLowerCase = true);
  49. }
  50. // } // namespace GM
  51. #endif // end __GM_UTIL_H__

gmutil.cpp文件:

  1. #include "gmutil.h"
  2. #include <iostream>
  3. #include<fstream>
  4. #include<sstream>
  5. #include <stdio.h>
  6. #include <openssl/evp.h>
  7. #include <openssl/pem.h>
  8. #include <openssl/sm2.h>
  9. #include <openssl/bio.h>
  10. //using namespace GM;
  11. /**
  12. * @brief 使用公钥/私钥数据获取EV_KEY对象
  13. * @param key 公钥/私钥数据
  14. * @param is_public 是否公钥
  15. * @return 失败返回NULL
  16. */
  17. static EC_KEY *CreateEC(unsigned char *key, int is_public)
  18. {
  19. EC_KEY *ec_key = NULL;
  20. BIO *keybio = NULL;
  21. keybio = BIO_new_mem_buf(key, -1);
  22. if (keybio == NULL) {
  23. printf("%s", "[BIO_new_mem_buf]->key len=%d,Failed to Get Key", strlen((char *) key));
  24. return NULL;
  25. }
  26. if (is_public) {
  27. ec_key = PEM_read_bio_EC_PUBKEY(keybio, NULL, NULL, NULL);
  28. } else {
  29. ec_key = PEM_read_bio_ECPrivateKey(keybio, NULL, NULL, NULL);
  30. }
  31. if (ec_key == NULL) {
  32. printf("Failed to Get Key");
  33. BIO_free_all(keybio);
  34. return NULL;
  35. }
  36. BIO_free_all(keybio); // 此处是不是要free?
  37. return ec_key;
  38. }
  39. int GmSm2Encrypt(string strPubKey, const string &strIn, string &strCiphertext)
  40. {
  41. EC_KEY *evKey = CreateEC((unsigned char *)strPubKey.c_str(), 1);
  42. if (NULL == evKey)
  43. {
  44. return GM_UTIL_CODE_CREATE_EV_KEY_FAILED;
  45. }
  46. // 目前只支持默认的sm2p256v1椭圆曲线参数
  47. if (!EC_KEY_is_sm2p256v1(evKey))
  48. {
  49. EC_KEY_free(evKey);
  50. return GM_UTIL_CODE_NOT_SM2P256V1;
  51. }
  52. // 加密后的密文会比明文长97字节
  53. unsigned char *buff = NULL;
  54. size_t outLen = 0;
  55. SM2CiphertextValue *cval = NULL;
  56. size_t mlen, clen;
  57. unsigned char *p;
  58. if (NULL == (cval = SM2_do_encrypt(EVP_sm3(), (const unsigned char *)strIn.c_str(), strIn.size(), evKey)))
  59. {
  60. EC_KEY_free(evKey);
  61. return GM_UTIL_CODE_SM2_ENCRYPT_FAILED;
  62. }
  63. BIO *bOut = BIO_new(BIO_s_mem());
  64. if (NULL == bOut)
  65. {
  66. EC_KEY_free(evKey);
  67. SM2CiphertextValue_free(cval);
  68. return GM_UTIL_CODE_INIT_BIO_FAILED;
  69. }
  70. if (i2d_SM2CiphertextValue_bio(bOut, cval) <= 0)
  71. {
  72. SM2CiphertextValue_free(cval);
  73. BIO_free(bOut);
  74. EC_KEY_free(evKey);
  75. return GM_UTIL_CODE_CIPHER_TEXT_TO_BIO_FAILED;
  76. }
  77. if (0 == (outLen = BIO_get_mem_data(bOut, (char **)&buff)))
  78. {
  79. SM2CiphertextValue_free(cval);
  80. BIO_free(bOut);
  81. EC_KEY_free(evKey);
  82. return GM_UTIL_CODE_BIO_DATA_TO_MEM_FAILED;
  83. }
  84. strCiphertext.assign((char *)buff, outLen);
  85. // 释放内存
  86. SM2CiphertextValue_free(cval);
  87. BIO_free(bOut);
  88. EC_KEY_free(evKey);
  89. // OPENSSL_free(buff); //此处释放会挂掉,不应该free,应该是在BIO_free的时候内存已经被释放掉
  90. return GM_UTIL_CODE_OK;
  91. }
  92. int GmSm2Decrypt(string strPriKey, const string &strCiphertext, string &strOut)
  93. {
  94. EC_KEY *evKey = CreateEC((unsigned char *)strPriKey.c_str(), 0);
  95. if (NULL == evKey)
  96. {
  97. return GM_UTIL_CODE_CREATE_EV_KEY_FAILED;
  98. }
  99. if (!EC_KEY_is_sm2p256v1(evKey))
  100. {
  101. EC_KEY_free(evKey);
  102. return GM_UTIL_CODE_NOT_SM2P256V1;
  103. }
  104. BIO *bIn = NULL;
  105. bIn = BIO_new_mem_buf(strCiphertext.c_str(), strCiphertext.size());
  106. if (bIn == NULL)
  107. {
  108. EC_KEY_free(evKey);
  109. return GM_UTIL_CODE_INIT_BIO_FAILED;
  110. }
  111. int ret = 0;
  112. SM2CiphertextValue *cval = NULL;
  113. void *buf = NULL;
  114. size_t siz;
  115. const EVP_MD* md = EVP_sm3();
  116. if (NULL == (cval = d2i_SM2CiphertextValue_bio(bIn, NULL)))
  117. {
  118. BIO_free(bIn);
  119. EC_KEY_free(evKey);
  120. return GM_UTIL_CODE_BIO_DATA_TO_CIPHER_TEXT_FAILED;
  121. }
  122. if (0 == SM2_do_decrypt(md, cval, NULL, &siz, evKey) || !(buf = OPENSSL_malloc(siz)))
  123. {
  124. BIO_free(bIn);
  125. SM2CiphertextValue_free(cval);
  126. EC_KEY_free(evKey);
  127. return GM_UTIL_CODE_SM2_DECRYPT_FAILED;
  128. }
  129. if (0 == SM2_do_decrypt(md, cval, (unsigned char*)buf, &siz, evKey))
  130. {
  131. BIO_free(bIn);
  132. SM2CiphertextValue_free(cval);
  133. OPENSSL_free(buf);
  134. EC_KEY_free(evKey);
  135. return GM_UTIL_CODE_SM2_DECRYPT_FAILED;
  136. }
  137. strOut.assign((char*)buf, siz);
  138. // 释放内存
  139. BIO_free(bIn);
  140. SM2CiphertextValue_free(cval);
  141. OPENSSL_free(buf);
  142. EC_KEY_free(evKey);
  143. return GM_UTIL_CODE_OK;
  144. }
  145. static streamsize Read(istream &stream, char *buffer, streamsize count)
  146. {
  147. streamsize reads = stream.rdbuf()->sgetn(buffer, count);
  148. stream.rdstate();
  149. stream.peek();
  150. return reads;
  151. }
  152. string GmReadKeyFromFile(string strFileName)
  153. {
  154. fstream myfile;
  155. myfile.open(strFileName, ifstream::in | ifstream::binary);
  156. if (!myfile.is_open())
  157. {
  158. return "";
  159. }
  160. char buff[1024];
  161. std::ostringstream oss;
  162. int len;
  163. while (!myfile.eof())
  164. {
  165. size_t read = Read(myfile, buff, sizeof(buff));
  166. oss << string(buff, read);
  167. }
  168. myfile.close();
  169. return oss.str();
  170. }
  171. static char sDigit1[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  172. static char sDigit2[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  173. string GmByte2HexStr(const string& data, bool bLowerCase)
  174. {
  175. char *sDigit = sDigit1;
  176. if (!bLowerCase)
  177. {
  178. sDigit = sDigit2;
  179. }
  180. const char* pData = data.c_str();
  181. char cTemp;
  182. string strHex;
  183. for (unsigned int i = 0; i < data.size(); i++)
  184. {
  185. cTemp = *pData;
  186. pData++;
  187. strHex += sDigit[(cTemp >> 4) & 0x0F];
  188. strHex += sDigit[cTemp & 0x0F];
  189. }
  190. return strHex;
  191. }
  192. string GmHexStr2Byte(const string& hex, bool bLowerCase)
  193. {
  194. if (hex.size() % 2 != 0)
  195. {
  196. // 十六进制字符串必须是偶数长度
  197. return "";
  198. }
  199. char chA = 'a';
  200. if (!bLowerCase)
  201. {
  202. chA = 'A';
  203. }
  204. std::ostringstream oss;
  205. for (int i = 0; i < hex.size(); i += 2)
  206. {
  207. unsigned int highBit;
  208. if (hex[i] >= '0' && hex[i] <= '9')
  209. {
  210. highBit = hex[i] - '0';
  211. }
  212. else
  213. {
  214. highBit = hex[i] - chA + 10;
  215. }
  216. unsigned int lowBit;
  217. if (hex[i + 1] >= '0' && hex[i + 1] <= '9')
  218. {
  219. lowBit = hex[i + 1] - '0';
  220. }
  221. else
  222. {
  223. lowBit = hex[i + 1] - chA + 10;
  224. }
  225. unsigned char ch = (highBit << 4) + lowBit;
  226. oss << ch;
  227. }
  228. return oss.str();
  229. }

下面是测试验证test.cpp:

  1. #include "gmutil.h"
  2. #include <iostream>
  3. #include <sstream>
  4. int main(int argc, char** argv)
  5. {
  6. string strPriKey = GmReadKeyFromFile("sm2_server_private_key.key");
  7. string strPubKey = GmReadKeyFromFile("sm2_server_public_key.key");
  8. string strText = "hello world, this is a test";
  9. string strCipher;
  10. string strOut;
  11. std::cout << "plaintext:" << strText << std::endl;
  12. int nRet = GmSm2Encrypt(strPubKey, strText, strCipher);
  13. if (GM_UTIL_CODE_OK != nRet)
  14. {
  15. cout << "GmSm2Encrypt fail" << endl;
  16. }
  17. string strCipherTextHex = GmByte2HexStr(strCipher);
  18. cout << "hex ciper text:" << strCipherTextHex << endl;
  19. string strCipher1 = GmHexStr2Byte(strCipherTextHex);
  20. if (strCipher1 == strCipher)
  21. {
  22. cout << "conver hex str to byte sucess" << endl;
  23. }
  24. nRet = GmSm2Decrypt(strPriKey, strCipher1, strOut);
  25. std::cout << "after decrypt:" << strOut << std::endl;
  26. return 0;
  27. }

编译的时候要加这几个关键的编译参数:-fvisibility=hidden -Wl,-Bsymbolic -Wl,--exclude-libs,ALL

用来屏蔽动态库中的符号的。

gmssl库相关函数的文档可以参考官网:国密SM2椭圆曲线密码标准 (gmssl.org)

还有可以直接参考gmssl源码中的sm2test.c以及sm2utl.c,里面有相关的代码

还有gmssl库用到openssl库的很多函数比如BIO等,可以参数openssl官网来使用

参考文章:

(20条消息) 基于GMSSL的SM2加解密测试_viqjeee的博客-CSDN博客_gmssl sm2

(20条消息) C语言SM2算法实现(基于GMSSL)_张志翔 ̮的博客-CSDN博客_sm2实现 --这个最详细,直接有完整代码

(25条消息) SM2算法全套(基于GMSSL)_wincent1的博客-CSDN博客_gmssl sm2算法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/388342
推荐阅读
相关标签
  

闽ICP备14008679号