zoukankan      html  css  js  c++  java
  • 基于Gmssl的SM2加解密算法Demo

    基于Gmssl的SM2加解密算法Demo

    存储小咖 2018-12-28 18:38:11 4739 收藏 5
    展开
    GmSSL介绍
    Gmssl介绍:http://gmssl.org/
    当然本文也是参考 http://gmssl.org/
    其中SM2为非对称算法

    SM2密钥生成

    pair<string, string> GenKey(void)
    {
    EC_KEY *keypair = NULL;
    EC_GROUP *group1 = NULL;

    keypair = EC_KEY_new();
    if(!keypair) {
    cout << "Failed to Gen Key" << endl;
    exit(1);
    }

    group1 = EC_GROUP_new_by_curve_name(NID_sm2p256v1);

    if(group1 == NULL){
    cout << "Failed to Gen Key" << endl;
    exit(1);
    }

    int ret1 = EC_KEY_set_group(keypair, group1);
    if(ret1 != 1){
    cout << "Failed to Gen Key" << endl;
    exit(1);
    }

    int ret2 = EC_KEY_generate_key(keypair);
    if(ret2 != 1){
    cout << "Failed to Gen Key" << endl;
    exit(1);
    }

    size_t pri_len;
    size_t pub_len;
    char *pri_key = NULL;
    char *pub_key = NULL;

    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);

    pri_key = new char[pri_len + 1];
    pub_key = new char[pub_len + 1];

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

    pri_key[pri_len] = '';
    pub_key[pub_len] = '';

    string public_key = pub_key;
    string private_key = pri_key;

    EC_KEY_free(keypair);
    BIO_free_all(pub);
    BIO_free_all(pri);
    delete [] pri_key;
    delete [] pub_key;

    return std::pair<string, string>(public_key, private_key);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    已字符串形式的返回,主要考虑我们在实际运用中,可能需要传递密钥信息,所以生成的SM2公钥和私钥以字符串形式返回。

    从字符串类型的密钥中生成出来EC_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) {
    cout << "Failed to Get Key" << endl;
    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) {
    cout << "Failed to Get Key" << endl;
    exit(1);
    }

    return ec_key;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    SM2公钥加密
    string Encrypt(const string& public_key, const string& plain_text)
    {
    unsigned char encrypted[1024] = {};

    EC_KEY *rsa = CreateEC((unsigned char*)public_key.c_str(), 1);
    size_t encrypted_length = 1024;
    int ret = SM2_encrypt_with_recommended((unsigned char*)plain_text.c_str(), plain_text.length(),
    (unsigned char*)encrypted,&encrypted_length, rsa);

    if (ret == 0) {
    cout << "Failed to Encrypt" << endl;
    exit(1);
    }

    string enc_text((char*)encrypted, encrypted_length);
    return enc_text;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    这里使用的加密函数为SM2_encrypt_with_recommended

    SM2私钥解密
    string Decrypt(const string& private_key, const string& enc_text)
    {
    unsigned char decrypted[1024] = {};

    EC_KEY * key1 = CreateEC((unsigned char*)private_key.c_str(), 0);

    size_t decrypted_length = 0;
    int ret = SM2_decrypt_with_recommended((unsigned char*)enc_text.c_str(), enc_text.length(), decrypted, &decrypted_length, key1);

    if (ret == 0) {
    cout << "Failed to Decrypt" << endl;
    exit(1);
    }

    string plain_text((char*)decrypted, decrypted_length);
    return plain_text;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    这里使用的解密函数为SM2_decrypt_with_recommended

    测试用例代码
    下载:https://download.csdn.net/download/liulangaliulang/10884044
    用例运行结果:

    ————————————————
    版权声明:本文为CSDN博主「存储小咖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/liulangaliulang/article/details/85329878

    SM2加解密算法(基于GMSSL的C代码实现)

     

    一、椭圆曲线密码算法

    • 椭圆曲线:是一类二元多项式方程,它的解构成一个椭圆曲线。

    • 椭圆曲线参数:定义一条唯一的椭圆曲线。介绍其中两个参数G(基点)和n(阶)。G点(xG, yG)是椭圆曲线上的基点, 有限域椭圆曲线上所有其他的点都可以通过G点的倍乘运算计算得到,即P=[d]G, d也是属于有限域,d的最大值为素数n。

    • 有限域上的椭圆曲线:椭圆曲线上的解不是连续的,而是离散的,解的值满足有限域的限制。有限域有两种,Fp和F2m。

    • E(Fq):Fq上椭圆曲线E 的所有有理点(包括无穷远点O)组成的集合。

    • Fp:一个素整数的集合,最大值为P-1,集合中的值都是素数,里面元素满足以下模运算: a+b=(a+b) mod p 和 ab=(ab) mod p。

    • SM2:有限域Fp上的一条椭圆曲线,其椭圆曲线参数是固定值。

    • 公私钥:P=[d]G,G是已知的,大数d为私钥,点P(XP, YP)为公钥。

    • SM2推荐使用素数域256位椭圆曲线:
      -->EC_GROUP_new_by_curve_name(NID_sm2p256v1)

    //可以得出固定参数
    //Sm2 中指定的参数 确定下y2 = x3 + ax + b 曲线
    #define _P  "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"
    #define _a  "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC"
    #define _b  "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"
    #define _n  "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
    #define _Gx "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"
    #define _Gy "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
    
    • OpenSSL部分涉及代码
    //初始化一个空算法组
    EC_GROUP *group = EC_GROUP_new(EC_GFp_mont_method());
    //初始化一个推荐椭圆曲线的算法组
    EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_sm2p256v1);
    //上下文
    BN_CTX *ctx = BN_CTX_new();
    //创建EC_KEY,使用推荐椭圆曲线
    EC_KEY *ec_key  = EC_KEY_new_by_curve_name(NID_sm2p256v1)
    //生成公钥私钥
    EC_KEY_generate_key(ec_key);
    //设置私钥
    EC_KEY_set_private_key(ec_key, d);
    //设置公钥
    EC_KEY_set_public_key(ec_key, P);
    //通过ec_key获取算法组
    EC_GROUP *ec_group = EC_KEY_get0_group(ec_key);
    //获取基点G
    EC_POINT * G = EC_GROUP_get0_generator(ec_group);
    //大数初始化
    BIGNUM *rand = BN_new();
    //EC_POINT初始化
    EC_POINT *P = EC_POINT_new(ec_group);
    //获取坐标点p的x,y值
    EC_POINT_get_affine_coordinates_GFp(ec_group,p,x,y,ctx);
    //Gets the order of a EC_GROUP -- n阶 对应上面固定参数的_n
    EC_GROUP_get_order(ec_group, order, ctx);
    //随机数生成
    do {
         BN_rand_range(rand,order);
    } while (BN_is_zero(rand));
    //大数转二进制
    int len = BN_bn2bin(bn, outChar);
    //获取坐标点p转大数bn
    EC_POINT_point2bn(ec_group, p, POINT_CONVERSION_COMPRESSED, bn, ctx);
    //点的乘积  lP = P * rand
    EC_POINT_mul(ec_group, lP, NULL, P, rand, ctx);
    //验证点C1是否在椭圆曲线上
    EC_POINT_is_on_curve(ec_group, c1, ctx);
    

    马上开始

    二、SM2加密算法(手动实现和使用GMSSL库实现)

    PS:加解密中,加密时椭圆曲线点C1转换方式必须和解密时椭圆曲线点C1转换方式一致,否则无法解出C1。

    1、手动实现

    • 流程


       
      image.png
    • 算法:

    1、产生随机数k, k的值从1到n-1;

        BIGNUM *n,*k;
        n = BN_new();
        k = BN_new();
        EC_GROUP_get_order(ec_group, n, ctx);
        do {
            BN_rand_range(k,n);
        } while (BN_is_zero(k));
    

    2、计算椭圆曲线点C1=[k]G=(x1,y1), 将C1使用EC_POINT_point2oct转换成比特串;

        //获取基点G
        const EC_POINT *G = EC_GROUP_get0_generator(ec_group);
        EC_POINT *c1 = NULL;
        c1 = EC_POINT_new(ec_group);
        unsigned char c1bin[65];
        unsigned long c1binlen = 65;
        EC_POINT_mul(ec_group, c1, NULL, G, k, ctx);
        EC_POINT_point2oct(ec_group, c1, POINT_CONVERSION_UNCOMPRESSED, c1bin, c1binlen, ctx);
    

    3、 验证公钥PB, 计算S=[h] PB,如果S是无穷远点,出错退出;

        EC_POINT_is_on_curve(ec_group, PB, ctx);
        EC_POINT_is_at_infinity(ec_group, s);
    

    4、计算(x2,y2)=[k] PB

        EC_POINT *tempPoint = EC_POINT_new(ec_group);
        BIGNUM *x2 = BN_new();
        BIGNUM *y2 = BN_new();
        EC_POINT_mul(ec_group, tempPoint, NULL, pb, k, ctx);
        EC_POINT_get_affine_coordinates_GFp(ec_group,
                                            tempPoint, x2, y2, ctx);
    

    5、计算t=KDF(x2||y2, klen), KDF是密钥派生函数,klen是明文长度。

        unsigned char x2y2[64] = {0};
        unsigned long x2y2len = 0;
        //x2||y2
        x2y2len += BN_bn2bin(x2, x2y2);
        x2y2len += BN_bn2bin(y2, &x2y2[32]);
        unsigned char t[klen];
        unsigned long tlen = klen;
        kdf(EVP_sm3(), x2y2, sizeof(x2y2), t, &tlen);
    
    //kdf方法
    void *kdf(const EVP_MD *md, const void *in, size_t inlen,
                  void *out, size_t *outlen)
    {
        EVP_MD_CTX ctx;
        uint32_t counter = 1;
        uint32_t counter_be;
        unsigned char dgst[EVP_MAX_MD_SIZE];
        unsigned int dgstlen;
        unsigned char *pout = out;
        size_t rlen = *outlen;
        size_t len;
        
        EVP_MD_CTX_init(&ctx);
        
        while (rlen > 0) {
            counter_be = cpu_to_be32(counter);
            counter++;
            
            EVP_DigestInit(&ctx, md);
            EVP_DigestUpdate(&ctx, in, inlen);
            EVP_DigestUpdate(&ctx, &counter_be, sizeof(counter_be));
            EVP_DigestFinal(&ctx, dgst, &dgstlen);
            
            len = dgstlen <= rlen ? dgstlen : rlen;
            memcpy(pout, dgst, len);
            rlen -= len;
            pout += len;
        }
        
        EVP_MD_CTX_cleanup(&ctx);
        return out;
    }
    

    6、计算C2=M^t (此处^为异或)

        unsigned char c2[tlen];
        unsigned long c2len = 0;
        for (int i = 0; i < tlen; i ++) {
            c2[i] = M[i] ^ t[i];
            c2len++;
        }
    

    7、 计算C3=Hash(x2||M||y2)

        unsigned char c3[32];
        unsigned long c3len = 32;
        unsigned char tempC3[x2y2len+klen];
        BN_bn2bin(x2, tempC3);
        BN_bn2bin(y2, &tempC3[32+klen]);
        memcpy(&tempC3[32], M, klen);
        sm3(tempC3, x2y2len+klen, c3);
    

    8、 输出密文C=C1||C3||C2。

        unsigned char c[c1binlen + c2len + c3len];
        unsigned long clen = c1binlen + c2len + c3len;
        memcpy(c, c1bin, c1binlen);
        memcpy(&c[c1binlen], c3, c3len);
        memcpy(&c[c1binlen+c3len], c2, c2len);
    

    注:密文分为C1,C2,C3,三部分,C1长度是65字节(具体根据转换方式),C2是明文的长度,C3是32字节(Hash使用sm3)。
    注:C1 || C2 || C3 的意思就是拼在一起,而不是做什么或运算

    • 根据国密推荐的SM2椭圆曲线公钥密码算法,首先产生随机数计算出曲线点C1,2个32byte的BIGNUM大数,即为SM2加密结果的第1部分(C1)。第2部分则是真正的密文,是对明文的加密结果,长度和明文一样(C2)。第3部分是杂凑值,用来效验数据(C3)。按国密推荐的256位椭圆曲线,明文加密结果比原长度会大97byte(C1使用EC_POINT_point2oct转换)。
    • 注:通过密钥派生函数计算,才能进行第6步的按位异或计算。

    2、使用GMSSL库实现

    • 基于GmSSL 1.2.2 (OpenSSL 1.0.2d)
    /**
     修复BN_bn2bin函数结果不为32的情况(高位补0)
     @param sourceBn 源BIGNUM
     @param out 输出
     @return 返回长度
     */
    int fixBn2Bin(BIGNUM *sourceBn,unsigned char *out){
        unsigned char tempBin[32] = {0};
        int tempLen = BN_bn2bin(sourceBn, tempBin);
        if (tempLen != 32) {
            memset(out, 0, 32 - tempLen);
        }
        memcpy(&out[32-tempLen], tempBin, tempLen);
        return 32;
    }
    #define RV_OK  0x00000000  //success
    #define RV_EncErr  0x01000010  //encrypt error
    /**
     SM2加密 使用国密GMSSL库
    
     @param P 公钥
     @param encryptData 需要加密的数据
     @param outData 输出加密后的数据
     @param outDataLen 输出加密后的数据长度
     @return 0成功/其它失败
     */
    int sm2EncryptByGMSSL(EC_POINT *P,unsigned char *encryptData,unsigned long encryptDataLen,unsigned char *outData,unsigned long *outDataLen){
        int resultCode = RV_OK;
        //变量
        EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_sm2p256v1);
        EC_KEY_set_public_key(ec_key, P);
        const EC_GROUP *ec_group = EC_KEY_get0_group(ec_key);
    
        BN_CTX *ctx = BN_CTX_new();
        SM2_CIPHERTEXT_VALUE *cv = NULL;
        SM2_ENC_PARAMS params;
    
        //c1
        BIGNUM *x = NULL;
        BIGNUM *y = NULL;
        unsigned char *c1Buf = NULL;
        unsigned long c1Len = 0;
        
        //开始加密(使用GMSSL的方法)
        SM2_ENC_PARAMS_init_with_recommended(&params);
        if (!(cv = SM2_do_encrypt(&params, encryptData, encryptDataLen, ec_key))) {
            resultCode = RV_EncErr;
            goto end;
        }
        OPENSSL_assert(cv);
        //C1
        //确定c1所需空间
        if (!(c1Len = EC_POINT_point2oct(ec_group, cv->ephem_point,
                                         params.point_form, NULL, 0, ctx))) {
            resultCode = RV_EncErr;
            goto end;
        }
        //创建c1所需空间
        c1Buf = malloc(c1Len);
        if (!(c1Len = EC_POINT_point2oct(ec_group, cv->ephem_point,
                                         params.point_form, c1Buf, c1Len, ctx))) {
            resultCode = RV_EncErr;
            goto end;
        }
        memcpy(outData, c1Buf, c1Len);
        //获取C1方法二
    //    unsigned char c1Bin[64] = {0};
    //    x = BN_new();
    //    y = BN_new();
    //    EC_POINT_get_affine_coordinates_GFp(ec_group,cv->ephem_point, x, y, ctx);
    //
    //    fixBn2BinLen(x,c1Bin);
    //    fixBn2BinLen(y,&c1Bin[32]);
    //    //拼接C1
    //    memcpy(outData, c1Bin, 64);
        
        //后续拼接
        //拼接C3
        memcpy(outData+c1Len, cv -> mactag, cv->mactag_size);
        //拼接C2
        memcpy(outData+c1Len+cv->mactag_size, cv ->ciphertext, cv->ciphertext_size);
        *outDataLen = c1Len + cv->mactag_size + cv->ciphertext_size;
        
    end:
        EC_KEY_free(ec_key);
        BN_CTX_free(ctx);
        if(cv != NULL) SM2_CIPHERTEXT_VALUE_free(cv);
        if(x != NULL) BN_free(x);
        if(y != NULL) BN_free(y);
        if (c1Buf != NULL) {
            free(c1Buf);
        }
        return resultCode;
    }
    
    • 基于GmSSL 2.5.4 - OpenSSL 1.1.0d 3 Sep 2019
    /**
     使用gmssl SM2加密
     
     @param inData 需要加密的数据
     @param inDataLen 需要加密的数据长度
     @param pubKey 公钥(point2oct)
     @param pubKeyLen 公钥长度
     @param encryptData 加密后的数据
     @return 0:成功/非0:失败
     */
     int sm2EncryptByGMSSL(
                           unsigned char *inData,
                           unsigned long inDataLen,
                           unsigned char *pubKey,
                           unsigned long pubKeyLen,
                           SM2CiphertextValue **encryptData)
    {
        int resultCode = RV_OK;
        //公钥
        EC_KEY *ec_key = NULL;
        //公钥
        EC_POINT *publicKey = NULL;
        //ec_group
        EC_GROUP *ec_group = NULL;
        //ctx
        BN_CTX *ctx = NULL;
    
        
        //判断输入参数是否为空
        if (inData == NULL || inDataLen == 0 || pubKey == NULL || pubKeyLen == 0 || encryptData == NULL) {
            resultCode = RV_InputErr;
            goto err;
        }
        
        //恢复公钥
        ctx = BN_CTX_new();
        ec_group = EC_GROUP_new_by_curve_name(NID_sm2p256v1);
        publicKey = EC_POINT_new(ec_group);
        int mark = EC_POINT_oct2point(ec_group, publicKey, pubKey, pubKeyLen, ctx);
        if (mark != 1) {
            resultCode = RV_EncErr;
            goto err;
        }
        
        //初始化数据
        ec_key = EC_KEY_new_by_curve_name(NID_sm2p256v1);
        EC_KEY_set_public_key(ec_key, publicKey);
        ec_group = EC_GROUP_new_by_curve_name(NID_sm2p256v1);
        ctx = BN_CTX_new();
        
        //开始加密(使用GMSSL的方法)
        if (!(*encryptData = SM2_do_encrypt(EVP_sm3(), inData, inDataLen, ec_key))) {
            resultCode = RV_EncErr;
            goto err;
        }
        
    err:
        if (ec_key != NULL) {
            EC_KEY_free(ec_key);
        }
        if (ec_group != NULL) {
            EC_GROUP_free(ec_group);
        }
        if (ctx != NULL) {
            BN_CTX_free(ctx);
        }
        if (publicKey != NULL) {
            EC_POINT_free(publicKey);
        }
        return resultCode;
    }
    

    三、SM2解密算法

    1、手动实现

    • 流程


       
      image.png
    • 算法:

    1、从密文比特串C=C1||C3||C2中取出C1, 将C1转换成椭圆曲线上的点;

        #define POINT_BIN_LENGTH 65
    
        unsigned char c1Bin[POINT_BIN_LENGTH];
        unsigned long c1Binlen = POINT_BIN_LENGTH;
        memcpy(c1Bin, encrypt(密文), POINT_BIN_LENGTH);
        EC_POINT  *c1 = EC_POINT_new(ec_group);
        EC_POINT_oct2point(ec_group, c1, c1Bin, c1Binlen, ctx);
    

    2、验证C1, 计算S=[h] C1,如果S是无穷远点,出错退出;

        int resultCode = EC_POINT_is_on_curve(ec_group, c1, ctx);
        if (resultCode) {
            printf("验证C1成功
    ");
        }else{
            printf("验证C1失败
    ");
        }
    

    3、计算(x2,y2)=[dB] C1

        EC_POINT *dC1 = EC_POINT_new(ec_group);
        EC_POINT_mul(ec_group, dC1, NULL, c1, d, ctx);
        BIGNUM *x2 = BN_new();
        BIGNUM *y2 = BN_new();
        EC_POINT_get_affine_coordinates_GFp(ec_group,
                                            dC1, x2, y2, ctx);
    

    4、计算t=KDF(x2||y2, klen), KDF是密钥派生函数,如果t是全0比特串,出错退出。

        unsigned char x2y2[64] = {0};
        unsigned long x2y2len = 0;
        //x2||y2
        x2y2len += BN_bn2bin(x2, x2y2);
        x2y2len += BN_bn2bin(y2, &x2y2[32]);
        //原文长度klen
        unsigned long klen = encryptLen - (c1Binlen+c3len);
        
        unsigned char t[klen];
        unsigned long tlen = klen;
        sm3_kdf1(EVP_sm3(), x2y2, sizeof(x2y2), t, &tlen);
    

    5、从C=C1||C3||C2中取出C2,计算M’= C2+t。

        unsigned char c2[tlen];
        memcpy(c2, encrypt+c1Binlen+c3len, tlen);
        
        //原文
        unsigned char M[tlen+1];
        unsigned long Mlen = 0;
        for (int i = 0; i < tlen; i ++) {
            M[i] = c2[i] ^ t[i];
            Mlen++;
        }
        M[tlen] = '';
        printf("M'-->%s
    ",M);
    

    6、计算u=Hash(x2||M’||y2),比较u是否与C3相等,不相等则退出。
    7、输出明文M’。

    2、使用GMSSL库实现

    • 基于GmSSL 1.2.2 (OpenSSL 1.0.2d)
    
    #define RV_DecErr  0x01000011  //decrypt error
    
    /**
     使用GMSSL解密
    
     @param d 私钥
     @param encrypt 加密数据
     @param encryptLen 加密数据长度
     @param decryptData 解密数据
     @param decryptDataLen 解密数据长度
     @return 0成功/其它失败
     */
    int sm2DecryptByGMSSL(BIGNUM *d,unsigned char *encrypt,unsigned long encryptLen,unsigned char *decryptData,unsigned long *decryptDataLen)
    {
        int resultCode = 0;
        
        BN_CTX *ctx = NULL;
        SM2_CIPHERTEXT_VALUE *cv = NULL;
        //C1+C3大小(不包含ciphertext_size,即C2)
        int cvLen = 0;
        SM2_ENC_PARAMS params;
        
        EC_GROUP *ec_group = NULL;
        EC_KEY *ec_key = NULL;
        //bn_prime
        BIGNUM *prime = NULL;
        //c1长度
        int c1Len = 0;
        
        //初始化
        ctx = BN_CTX_new();
        SM2_ENC_PARAMS_init_with_recommended(&params);
        ec_group = EC_GROUP_new_by_curve_name(NID_sm2p256v1);
        ec_key = EC_KEY_new();
        EC_KEY_set_group(ec_key, ec_group);
        EC_KEY_set_private_key(ec_key, d);
        prime = BN_new();
        BN_hex2bn(&prime,_n);
        
        //获取C1(C = C1||C3||C2)-- C为加密数据encryptData
        if (!(cvLen = SM2_CIPHERTEXT_VALUE_size(ec_group, &params, 0))) {
            resultCode = RV_DecErr;
            goto end;
        }
        
        if (!(cv = OPENSSL_malloc(sizeof(SM2_CIPHERTEXT_VALUE)))) {
            resultCode = RV_DecErr;
            goto end;
        }
        
        cv->ephem_point = EC_POINT_new(ec_group);
        cv->ciphertext_size = encryptLen - cvLen;
        cv->ciphertext = OPENSSL_malloc(cv->ciphertext_size);
        if (!cv->ephem_point || !cv->ciphertext) {
            resultCode = RV_DecErr;
            goto end;
        }
        int macTagSize = params.mactag_size<0 ? EVP_MD_size(params.mac_md) : params.mactag_size;
        c1Len = cvLen - macTagSize;
        
        if (!EC_POINT_oct2point(ec_group, cv->ephem_point, encrypt, c1Len, ctx)) {
            resultCode = RV_DecErr;
            goto end;
        }
        
        cv->mactag_size = macTagSize;
        if (cv->mactag_size > 0) {
            memcpy(cv->mactag, encrypt + c1Len, cv->mactag_size);
        }
        
        memcpy(cv->ciphertext, encrypt + c1Len + cv->mactag_size, cv->ciphertext_size);
        
        if (!SM2_do_decrypt(&params, cv, decryptData, decryptDataLen, ec_key))
        {
            resultCode = RV_DecErr;
            goto end;
        }
        *decryptDataLen = cv->ciphertext_size;
       
    end:
        if (ctx != NULL) {
            BN_CTX_free(ctx);
        }
        if (cv != NULL) {
            SM2_CIPHERTEXT_VALUE_free(cv);
        }
        if (ec_group != NULL) {
            EC_GROUP_free(ec_group);
        }
        if (ec_key != NULL) {
            EC_KEY_free(ec_key);
        }
        if (prime != NULL) {
            BN_free(prime);
        }
        return resultCode;
    }
    
    • 基于GmSSL 2.5.4 - OpenSSL 1.1.0d 3 Sep 2019
    /**
     使用GMSSL解密
     
     @param cv 加密数据
     @param d 私钥
     @param decryptData 解密数据
     @param decryptDataLen 解密数据长度
     @return 0成功/其它失败
     */
      int sm2DecryptByGMSSL(SM2CiphertextValue *cv,BIGNUM *d,unsigned char *decryptData,unsigned long *decryptDataLen)
    {
        int resultCode = 0;
        BN_CTX *ctx = NULL;
        
        EC_GROUP *ec_group = NULL;
        EC_KEY *ec_key = NULL;
        //bn_prime
        BIGNUM *prime = NULL;
        
        //判断输入参数是否为空
        if (cv == NULL || d == NULL || decryptData == NULL) {
            resultCode = RV_InputErr;
            goto end;
        }
    
        
        //初始化
        ctx = BN_CTX_new();
        ec_group = EC_GROUP_new_by_curve_name(NID_sm2p256v1);
        //设置私钥
        ec_key = EC_KEY_new();
        EC_KEY_set_group(ec_key, ec_group);
        EC_KEY_set_private_key(ec_key, d);
        //prime
        prime = BN_new();
        BN_hex2bn(&prime,SM2_n);
        
        //C = C1||C3||C2 -- C为加密数据encryptData
        if (!SM2_do_decrypt(EVP_sm3(), cv, decryptData, decryptDataLen, ec_key))
        {
            resultCode = RV_DecErr;
            goto end;
        }
        printf("
     Decrypt Data-->%s
    ",decryptData);
    end:
        if (ctx != NULL) {
            BN_CTX_free(ctx);
        }
        if (ec_group != NULL) {
            EC_GROUP_free(ec_group);
        }
        if (ec_key != NULL) {
            EC_KEY_free(ec_key);
        }
        if (prime != NULL) {
            BN_free(prime);
        }
        if (d != NULL) {
            BN_free(d);
        }
        return resultCode;
    }
    

    四、结论

    • 想要成功解密出原文,必须是公钥PB和私钥dB是匹配的,即满足PB=[dB]G,原文经过两次与同一比特串的异或计算,结果还是原文。
  • 相关阅读:
    利用vuex 做个简单的前端缓存
    EFcore 解决 SQLite 没有datetime 类型的问题
    dotnet 清理 nuget 缓存
    .net 5 单文件模式发布异常 CodeBase is not supported on assemblies loaded from a single-file bundle
    ubuntu 开启ip转发的方法
    Vue-ECharts 6 迁移记录
    System.Text.Json 5.0 已增加支持将Enum 由默认 Number类型 转换为String JsonStringEnumConverter
    Windows 10 LTSC 2019 正式版轻松激活教程
    Mac 提示Permission denied
    苹果手机代理 charles 提示(此链接非私人连接)
  • 原文地址:https://www.cnblogs.com/bigben0123/p/12852501.html
Copyright © 2011-2022 走看看