C语言实现的SM2数字签名验证

    最近在工作中遇到一个需求:签名者使用密码设备(如加密机)生成了一些 SM2 签名,现在用户需要对签名进行验证,可是验签方没有密码设备,验签必须使用软件实现。在 CSDN 上找到了 goldboar 写的 SM2 签名及验签函数( http://download.csdn.net/detail/goldboar/3833072),但是不能直接使用。原因如下:

1) goldboar 的程序是自己生成 SM2 密钥对,用密钥对产生签名再验证签名,而现在需要使用外部传入的公钥、签名来做验签;

2) goldboar 的程序中使用的 ECC 参数是示例参数,不是 GM/T 0003.5-2012 规范中定义的参数;

3) goldboar 的程序是示例程序,没有对动态分配的内存进行仔细地清理,内存泄漏量不少。

 

   在《密码设备应用接口规范》中,对 SM2 公钥的数据结构定义如下:

#define  ECCref_MAX_BITS   512

#define  ECCref_MAX_LEN  ((ECCref_MAX_BITS+7) / 8)

typedef struct ECCrefPublicKey_st

{

   unsigned int  bits;

   unsigned char  x[ECCref_MAX_LEN];

   unsigned char  y[ECCref_MAX_LEN];

} ECCrefPublicKey;

  对 SM2 签名的数据结构定义如下:

typedef struct ECCSignature_st

{

  unsigned char  r[ECCref_MAX_LEN];

  unsigned char  s[ECCref_MAX_LEN];

} ECCSignature;

   可以看出公钥和签名都是以坐标分量的形式给出的。现在已经有以下信息:对原始数据的 SM3 哈希值(已经过预处理,对预处理的介绍可以看 http://blog.csdn.net/henter/article/details/13622601 )、SM2 公钥、SM2 签名,要根据这些信息来验证签名。

  于是在 goldboar 写的 SM2 程序的基础上,改写了一个纯粹用来做 SM2 签名验证的函数,编译时需要用到OpenSSL的头文件和库文件(libeay32.lib或libeay32.dll)。与 goldboar 的程序区别如下:
1) 仅用于做验签,不能签名;
2) 验签使用外部传入的 SM2 公钥,SM2 公钥以 (x,y) 坐标形式传入;
3) 签名也是以 (r,s) 坐标形式传入;
4) 增加了一些内存清理语句,内存泄漏有改善;
5) 验签函数中采用的椭圆曲线参数是规范 GM/T 0003.5-2012 中定义的参数。
6) 将一些对椭圆曲线参数的验证操作放入 _DEBUG 宏限制的范围内。因为参数是规范推荐的,已经过验证,所以在程序中无需再验证。将这些验证语句放入 _DEBUG 宏限制的范围内以后,如果编译 release 版本时就不会包含这些验证语句,效率可以有一点提升。

 

调用OpenSSL的椭圆曲线运算函数以后,总是会有一点内存泄漏,OpenSSL官方对于内存泄漏在网页 http://www.openssl.org/support/faq.html 上是这样解释的:

  I think I've detected a memory leak, is this a bug?
  In most cases the cause of an apparent memory leak is an OpenSSL internal table that is allocated when an application starts up. Since such tables do not grow in size over time they are harmless.
   从中可以看出,只要内存泄漏不随着调用函数次数的增加而增加,就不用担心。于是在 SM2 签名验签函数的基础上又写了一个测试函数,实验后发现如果反复验签多次,与只做一次验签相比,产生的内存泄漏数量没有增长,这就算是可以接受了。

    SM2 签名验签函数的下载网址是:http://download.csdn.net/detail/henter/7637429

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
SM2是一种基于椭圆曲线密码学的公钥密码算法,其实现相对复杂。以下是一个简单的C语言实现示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/bn.h> #include <openssl/ec.h> #include <openssl/ecdsa.h> #include <openssl/evp.h> #include <openssl/err.h> // 生成SM2密钥对 int generate_keypair(EC_KEY **ec_key) { int ret = -1; EC_KEY *key = NULL; EC_GROUP *group = NULL; const EVP_MD *md = NULL; key = EC_KEY_new(); if (key == NULL) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } group = EC_GROUP_new_by_curve_name(NID_sm2); if (group == NULL) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EC_KEY_set_group(key, group)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } md = EVP_sm3(); if (!ECDSA_set_method(key, ECDSA_sm2()) || !ECDSA_set_default_method(key, ECDSA_sm2())) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EC_KEY_generate_key(key)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } *ec_key = key; ret = 0; cleanup: if (group != NULL) { EC_GROUP_free(group); } if (key != NULL && ret != 0) { EC_KEY_free(key); } return ret; } // SM2加密 int sm2_encrypt(EC_KEY *ec_key, const unsigned char *plaintext, size_t plaintext_len, unsigned char *ciphertext, size_t *ciphertext_len) { int ret = -1; EC_GROUP *group = NULL; EC_POINT *pubkey = NULL; unsigned char *buf = NULL; size_t buf_len; const EVP_MD *md = NULL; group = EC_GROUP_new_by_curve_name(NID_sm2); if (group == NULL) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } pubkey = EC_KEY_get0_public_key(ec_key); if (pubkey == NULL) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } buf_len = EC_POINT_point2oct(group, pubkey, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); if (buf_len == 0) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } buf = malloc(buf_len); if (buf == NULL) { perror("malloc"); goto cleanup; } if (!EC_POINT_point2oct(group, pubkey, POINT_CONVERSION_UNCOMPRESSED, buf, buf_len, NULL)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } md = EVP_sm3(); if (!EVP_PKEY_CTX_set_ec_key(ec_key, md)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EVP_PKEY_encrypt_init(ec_key_ctx)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EVP_PKEY_CTX_set1_id(ec_key_ctx, buf, buf_len)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EVP_PKEY_encrypt(ec_key_ctx, ciphertext, ciphertext_len, plaintext, plaintext_len)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } ret = 0; cleanup: if (ec_key_ctx != NULL) { EVP_PKEY_CTX_free(ec_key_ctx); } if (buf != NULL) { free(buf); } if (group != NULL) { EC_GROUP_free(group); } return ret; } // SM2解密 int sm2_decrypt(EC_KEY *ec_key, const unsigned char *ciphertext, size_t ciphertext_len, unsigned char *plaintext, size_t *plaintext_len) { int ret = -1; EVP_PKEY_CTX *ec_key_ctx = NULL; const EVP_MD *md = NULL; md = EVP_sm3(); if (!EVP_PKEY_CTX_set_ec_key(ec_key, md)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } ec_key_ctx = EVP_PKEY_CTX_new(ec_key, NULL); if (ec_key_ctx == NULL) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EVP_PKEY_decrypt_init(ec_key_ctx)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } if (!EVP_PKEY_decrypt(ec_key_ctx, plaintext, plaintext_len, ciphertext, ciphertext_len)) { fprintf(stderr, "Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto cleanup; } ret = 0; cleanup: if (ec_key_ctx != NULL) { EVP_PKEY_CTX_free(ec_key_ctx); } return ret; } ``` 请注意,这只是一个基本示例。要实现完整的SM2算法,您需要考虑更多细节,例如密钥派生和数字签名验证。此外,您需要确保使用正确的SM2参数和安全实践来保护您的加密通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值