C语言SM2算法实现(基于GMSSL)

落爺英雄遲暮 提交于 2020-01-23 23:09:00

最近项目中需要通过C语言实现SM2、SM4国密算法,这里我基于GMSSL来进行实现,本人已在这5种环境下全部实现,并已使用在生产环境中。

1、GMSSL编译

GMSSL编译在不同环境下都不一样,这里我提供Window64、Arm64、Linux64、Android、himix200海思芯片 环境编译方法,传送门如下:

Gmssl官网地址

Gmssl 各平台编译方法【绝对可用】

如果各位都是比较懒得人,我这里也给各位提供上述五种环境已经编好的库,传送门如下:

Gmssl链接库(himix200、android、arm64、linux64、windows64)

2、SM2实现

我们基于第一步编译出来的库来实现我们的SM2算法,头文件相关代码(authref.h)如下:

#ifndef __AUTHREF_H__
#define __AUTHREF_H__
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SM2_PUB_KEY_SIZE     178        // SM2 公钥的位数

typedef struct SM2_KEY
{
	uint8_t *pub_key;
	uint8_t *pri_key;
} T_sm2_key;

typedef struct ENCRYPT_KEY
{
	uint8_t *text;
	int len;
} T_encrypt_key;

typedef struct DECRYPT_KEY
{
	uint8_t *text;
	int len;
} T_decrypt_key;

//得到椭圆曲线(EC_KEY)
// key – 密钥
// is_public – 是否公钥(0:否,1:是)
// return – 椭圆曲线(EC_KEY)
EC_KEY* CreateEC(unsigned char* key, int is_public);

// 生成SM2 Key
// return – SM2密钥(T_sm2_key)
T_sm2_key GenKey(void);

// SM2加密
// public_key – 公钥
// plain_text – 明文
// pResOut – 返回值
int Encrypt(const char *public_key, uint8_t plain_text[UID_LEN], int plain_len);

// SM2解密
// public_key – 私钥
// plain_text – 密文
// pResOut – 返回值
int Decrypt(const char *private_key, T_encrypt_key *enc_text, T_decrypt_key *pResOut);

#ifdef __cplusplus
}
#endif

#endif //__AUTHREF_H__

具体实现代码(authref.c)如下:

#include "authref.h"
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/sm2.h>

T_sm2_key GenKey(void) {
    EC_KEY *keypair = NULL;
    EC_GROUP *group1 = NULL;

    keypair = EC_KEY_new();
    if (!keypair) {
        printf("%s", "Failed to Gen Key");
        exit(1);
    }

    group1 = EC_GROUP_new_by_curve_name(NID_sm2p256v1);

    if (group1 == NULL) {
        printf("%s", "Failed to Gen Key");
        exit(1);
    }

    int ret1 = EC_KEY_set_group(keypair, group1);
    if (ret1 != 1) {
        printf("%s", "Failed to Gen Key");
        exit(1);
    }

    int ret2 = EC_KEY_generate_key(keypair);
    if (ret2 != 1) {
        printf("%s", "Failed to Gen Key");
        exit(1);
    }

    int pri_len;
    int pub_len;


    BIO *pri = BIO_new(BIO_s_mem());
    BIO *pub = BIO_new(BIO_s_mem());

    PEM_write_bio_ECPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
    PEM_write_bio_EC_PUBKEY(pub, keypair);

    pri_len = BIO_pending(pri);
    pub_len = BIO_pending(pub);

    char *pri_key = NULL;
    char *pub_key = NULL;

    pri_key = malloc(pri_len + 1);
    pub_key = malloc(pub_len + 1);

    BIO_read(pri, pri_key, pri_len);
    BIO_read(pub, pub_key, pub_len);

    pri_key[pri_len] = '\0';
    pub_key[pub_len] = '\0';
    T_sm2_key key = {pub_key, pri_key};

    EC_KEY_free(keypair);
    BIO_free_all(pub);
    BIO_free_all(pri);
    return key;
}

EC_KEY *CreateEC(unsigned char *key, int is_public) {
    EC_KEY *ec_key = NULL;
    BIO *keybio = NULL;
    keybio = BIO_new_mem_buf(key, -1);

    if (keybio == NULL) {
        printf("%s", "[BIO_new_mem_buf]->key len=%d,Failed to Get Key", strlen((char *) key));
        exit(1);
    }

    if (is_public) {
        ec_key = PEM_read_bio_EC_PUBKEY(keybio, NULL, NULL, NULL);
    } else {
        ec_key = PEM_read_bio_ECPrivateKey(keybio, NULL, NULL, NULL);
    }

    if (ec_key == NULL) {
        printf("Failed to Get Key");
        exit(1);
    }

    return ec_key;
}

unsigned char encrypted[1024] = {};
size_t encrypted_length = 1024;
int Encrypt(const char *public_key, uint8_t plain_text[UID_LEN], int plain_len) {
    int nRet = 0;
    if (plain_text) {
        EC_KEY *rsa = CreateEC((unsigned char *) public_key, 1);
        int encrypt = SM2_encrypt(NID_sm3, plain_text, plain_len,
                                  encrypted, &(encrypted_length), rsa);
        if (encrypt == 0) {
            printf("%s\n", "Failed to Encrypt");
            nRet = -1;
        }
    }
    return nRet;
}

int Decrypt(const char *private_key, T_encrypt_key *enc_text, T_decrypt_key *pResOut) {
    int nRet = 0;
    if (pResOut && enc_text) {
        EC_KEY *key1 = CreateEC((unsigned char *) private_key, 0);
        pResOut->text = (unsigned char *) malloc(enc_text->len);
        memset(pResOut->text, 0, enc_text->len);

        int ret = SM2_decrypt_with_recommended(enc_text->text, enc_text->len, pResOut->text,
                                               &(pResOut->len), key1);
        if (ret == 0) {
            printf("%s", "Failed to Decrypt");
            nRet = -1;
        }
    }
    return nRet;
}

到此我们就实现了基于GMSSL的SM2算法。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!