【加密算法】SM2国密算法原理、C++跨平台实现(含完整代码和示例)

引言

SM2是我国自主研发的非对称加密算法(国密算法),于2010年由国密局发布,2017年成为ISO/IEC国际标准。其基于椭圆曲线密码学(ECC),具备安全性高(256位密钥强度相当于RSA-3072位)、运算速度快、密钥短(256位)等优势,广泛应用于数字签名、公钥加密、密钥交换等领域,是金融、政务、物联网等场景的核心安全技术。本文从算法原理到代码实现,系统解析SM2的核心技术。


一、SM2算法基础

1.1 算法概述

SM2属于非对称加密算法,包含三个核心功能:

  • 数字签名:基于椭圆曲线离散对数难题(ECDLP),保障数据来源认证与完整性
  • 公钥加密:使用接收方公钥加密数据,私钥解密
  • 密钥交换:通过ECDH协议生成共享密钥,用于后续对称加密

1.2 核心数学原理

SM2的安全性依赖于椭圆曲线离散对数问题:已知椭圆曲线点P和Q=kP,求k在计算上不可行。其数学基础包括:

  • 有限域运算:素域Fp上的加法、乘法、逆运算
  • 椭圆曲线方程:标准方程为 ( y^2 = x^3 + ax + b \mod p ),推荐参数为256位素数域
  • 点运算:点加(P+Q)、倍点(2P)和标量乘法(kP)构成核心运算

1.3 核心参数

国密推荐曲线参数如下(十六进制):

  • 素数p:FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
  • 系数a:FFFFFFFE ... FFFFFFFC
  • 基点G:(x, y)坐标,阶n为FFFFFFFE ... 39D54123

1.4 算法组件

SM2包含四大核心算法模块:

  1. 密钥生成:随机数d∈[1,n-1]作为私钥,公钥P=dG
    开始
    选择椭圆曲线参数\(p,a,b,G,n,h)
    生成随机数d∈$$1,n-1$$
    计算公钥P=d*G
    验证P≠O ?
    输出密钥对
    (私钥d, 公钥P)
    :根据上述密钥生成的流程,是先生成私钥d,然后计算公钥P,因此在SM2的密钥中,可以根据私钥计算出公钥
  2. 数字签名:使用私钥对消息哈希值签名,生成(r,s)
  3. 加密/解密:基于KDF生成会话密钥,结合椭圆曲线点运算
  4. 密钥协商:通过两次握手协议生成共享密钥

二、算法原理详解

2.1 数字签名流程

签名生成

  1. 计算消息哈希值e=HASH(Z_A || M),Z_A为用户身份标识
  2. 生成随机数k∈[1,n-1],计算点(x1,y1)=kG
  3. r=(e + x1) mod n,若r=0则重新选k
  4. s=((1+d_A)^-1 * (k - r*d_A)) mod n,输出签名(r,s)
输入: 消息M, 私钥dA
计算哈希值e=SM3(ZA||M)
生成随机数k ∈ [1,n-1]
计算椭圆曲线点(x₁,y₁)=k*G
计算r=(e + x₁) mod n
r=0 或 r+k=n?
计算s=((1+dA)⁻¹*(k - r*dA)) mod n
s=0?
输出签名(r,s)

验签流程

  1. 验证r,s∈[1,n-1]
  2. 计算t=(r+s) mod n,点(x1,y1)=sG + tP_A
  3. 验证r ≡ (e + x1) mod n

2.2 加密与解密流程

加密

  1. 生成随机数k,计算C1=kG(椭圆曲线点)
  2. 计算S=h*P_B(h为余因子),若S为无穷远点则报错
  3. 计算(x2,y2)=kP_B,通过KDF生成密钥t
  4. 密文C2=M⊕t,C3=HASH(x2||M||y2)
  5. 输出C=C1||C3||C2
输入: 明文M, 公钥PB
生成随机数k ∈ [1,n-1]
计算C₁=k*G
计算椭圆曲线点S=h*PB
S是无穷远点?
计算(x₂,y₂)=k*PB
生成密钥t=KDF(x₂||y₂, len(M))
计算密文C₂=M⊕t
计算C₃=SM3(x₂||M||y₂)
输出密文C=C₁||C₃||C₂
_注:h为余因子(通常为1),KDF为密钥派生函数。_

解密

  1. 验证C1在曲线上,计算S=h*C1
  2. 计算(x2,y2)=d_B*C1,生成t=KDF(x2||y2)
  3. 解密M’=C2⊕t,验证C3=HASH(x2||M’||y2)

2.3 密钥交换协议

基于改进的ECDH协议,两轮交互生成共享密钥:

  1. 双方生成临时密钥对(r_A, R_A)和(r_B, R_B)
  2. 计算共享点U=r_A(r_B G + P_B) 和 U’=r_B(r_A G + P_A)
  3. 通过KDF处理U的坐标生成会话密钥
User B
发送RB给User A
生成临时私钥rB, 计算RB=rB*G
接收RA
计算U'=rB*(RA + PA)
生成共享密钥K=KDF(xU'||yU'||ZA||ZB)
User A
发送RA给User B
生成临时私钥rA, 计算RA=rA*G
接收RB
计算U=rA*(RB + PB)
生成共享密钥K=KDF(xU||yU||ZA||ZB)

2.4 椭圆曲线数学优化

  • 坐标系选择:Jacobian射影坐标减少模逆运算
  • 标量乘法优化:采用滑动窗口法、NAF编码降低计算量
  • 预计算技术:固定基点G的预计算表加速kG运算

三、算法实现技术

3.1 硬件实现

  • 专用芯片:如HS32U2-U,通过SPI接口实现低功耗加密
  • FPGA优化:流水线设计SM2协处理器,时钟频率达50MHz,标量乘仅0.869ms
  • 抗侧信道技术:随机化NAF窗口、盲化标量防御SPA/DPA攻击

3.2 软件实现

  • Bouncy Castle库:Java示例代码展示密钥生成与加密
  • OpenSSL引擎:集成SM2到EVP接口,支持标准API调用
  • 性能优化技巧
    • 预计算常用点坐标(如16个预计算点加速kP)
    • SIMD指令加速有限域乘法(如Intel AVX2)

四、安全性分析

4.1 算法设计安全性

  • 数学安全性:基于ECDLP难题,256位密钥抗量子攻击优于RSA-3072
  • 抗碰撞性:SM3哈希保障签名与加密数据完整性
  • 标准化认证:通过国密GM/T 0003.5-2012、ISO/IEC 14888-3

4.2 抗攻击能力

  • 侧信道防护:随机化标量乘顺序、加入噪声指令
  • 故障注入防御:双点校验、错误感染技术
  • 弱曲线防御:强制验证公共参数防止参数替换攻击
    抗攻击防御体系

4.3 与RSA/AES对比

特性 SM2 RSA-3072 AES-256
密钥长度 256位 3072位 256位
安全强度 抗量子 不抗量子 抗量子
签名速度 0.98ms(FPGA) 3.2ms N/A
适用场景 非对称加密 传统非对称加密 对称加密

五、C++跨平台自实现(Header-Only

5.1 源码封装

/**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​***
* Copyright (c) 2025, arbboter. All rights reserved.
* File    : filename.cpp
* Desc    : 本文件实现SM2模块的核心算法
*           - SM2的签名和验签
*           - SM2的加密和解密
* Version : 1.0.0.1
* Date    : 2025-04-11
* Author  : arbboter@hotmail.com
* License : SPDX-License-Identifier: Apache-2.0 (详见 LICENSE 文件)
* 
* ------------------------- Revision History --------------------------------
* <Version>     <Date>        <Author>               <Modification Description>
*  1.0.0.1      2025-01-01   arbboter@hotmail.com    初始版本创建
​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​**​​***/
#ifndef __SM2_H__
#define __SM2_H__
#include <stdio.h>
#include <string.h>
#include <cstdint>

#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0)
#endif
#else
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif


#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) \
		{
      
      \
			(n) = ((unsigned int)(b)[(i)    ] << 24 ) \
				| ((unsigned int)(b)[(i) + 1] << 16 ) \
				| ((unsigned int)(b)[(i) + 2] <<  8 ) \
				| ((unsigned int)(b)[(i) + 3]       );\
		}
#endif

#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) \
		{
      
      \
			(b)[(i)    ] = (unsigned char) ( (n) >> 24 );\
			(b)[(i) + 1] = (unsigned char) ( (n) >> 16 );\
			(b)[(i) + 2] = (unsigned char) ( (n) >>  8 );\
			(b)[(i) + 3] = (unsigned char) ( (n)       );\
		}
#endif

#define FF0(x,y,z) ( (x) ^ (y) ^ (z)) 
#define FF1(x,y,z) (((x) & (y)) | ( (x) & (z)) | ( (y) & (z)))

#define GG0(x,y,z) ( (x) ^ (y) ^ (z)) 
#define GG1(x,y,z) (((x) & (y)) | ( (~(x)) & (z)) )

#define SHL(x,n) (((x) & 0xFFFFFFFF) << n)
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))

#define P0(x) ((x) ^  ROTL((x),9) ^ ROTL((x),17)) 
#define P1(x) ((x) ^  ROTL((x),15) ^ ROTL((x),23))

#define GETU32(p) ((uint32_t)(p)[0] << 24 | (uint32_t)(p)[1] << 16 | (uint32_t)(p)[2] <<  8 | (uint32_t)(p)[3])
#define PUTU32(p,V) ((p)[0] = (uint8_t)((V) >> 24), (p)[1] = (uint8_t)((V) >> 16), (p)[2] = (uint8_t)((V) >>  8), (p)[3] = (uint8_t)(V))

#define SM2_MIN_PLAINTEXT_SIZE 1
#define SM2_MAX_PLAINTEXT_SIZE 3072
#define SM2_DEFAULT_ID "1234567812345678"
#define RAND_MAX_BUF_SIZE 256
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif

#define ASN1_TAG_INTEGER 2
#define ASN1_TAG_OCTET_STRING 4
#define ASN1_TAG_SEQUENCE 0x30

typedef uint64_t SM2_BN[8];
typedef SM2_BN SM2_Fp;
typedef SM2_BN SM2_Fn;

typedef struct
{
    
    
	uint8_t x[32];
	uint8_t y[32];
} SM2_POINT;

typedef struct
{
    
    
	SM2_POINT public_key;
	uint8_t private_key[32];
} SM2_KEY;

typedef struct
{
    
    
	uint8_t r[32];
	uint8_t s[32];
} SM2_SIGNATURE;

// 雅可比行列式
typedef struct 
{
    
    
	SM2_BN X;
	SM2_BN Y;
	SM2_BN Z;
} SM2_JACOBIAN_POINT;

typedef struct 
{
    
    
	SM2_POINT point;
	uint8_t hash[32];
	uint32_t ciphertext_size;
	uint8_t ciphertext[SM2_MAX_PLAINTEXT_SIZE];
} SM2_CIPHERTEXT;

const SM2_BN SM2_P = 
{
    
    
	0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
	0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
};

const SM2_BN SM2_B = 
{
    
    
	0x4d940e93, 0xddbcbd41, 0x15ab8f92, 0xf39789f5,
	0xcf6509a7, 0x4d5a9e4b, 0x9d9f5e34, 0x28e9fa9e,
};

const SM2_JACOBIAN_POINT _SM2_G = 
{
    
    
	{
    
    
		0x334c74c7, 0x715a4589, 0xf2660be1, 0x8fe30bbf,
		0x6a39c994, 0x5f990446, 0x1f198119, 0x32c4ae2c,
	},
	{
    
    
		0x2139f0a0, 0x02df32e5, 0xc62a4740, 0xd0a9877c,
		0x6b692153, 0x59bdcee3, 0xf4f6779c, 0xbc3736a2,
	},
	{
    
    
		1, 0, 0, 0, 0, 0, 0, 0,
	},
};
const SM2_JACOBIAN_POINT* SM2_G = &_SM2_G;

const SM2_BN SM2_N =
{
    
    
	0x39d54123, 0x53bbf409, 0x21c6052b, 0x7203df6b,
	0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
};

const SM2_BN SM2_ONE = {
    
     1,0,0,0,0,0,0,0 };
const SM2_BN SM2_TWO = {
    
     2,0,0,0,0,0,0,0 };

typedef struct
{
    
    
	unsigned int total[2];		// number of bytes processed
	unsigned int state[8];		// intermediate digest state
	unsigned char buffer[64];	// data block being processed
	unsigned char ipad[64];		// HMAC: inner padding
	unsigned char opad[64];		// HMAC: outer padding
} sm3_context;


class SM3
{
    
    
public:
	static void Starts(sm3_context* ctx)
	{
    
    
		ctx->total[0] = 0;
		ctx->total[1] = 0;

		ctx->state[0] = 0x7380166F;
		ctx->state[1] = 0x4914B2B9;
		ctx->state[2] = 0x172442D7;
		ctx->state[3] = 0xDA8A0600;
		ctx->state[4] = 0xA96F30BC;
		ctx->state[5] = 0x163138AA;
		ctx->state[6] = 0xE38DEE4D;
		ctx->state[7] = 0xB0FB0E4E;
	}

	static void Update(sm3_context* ctx, unsigned char* input, size_t ilen)
	{
    
    
		unsigned int fill = 0;
		unsigned int left = 0;

		if (ilen == 0) {
    
     return; }

		left = ctx->total[0] & 0x3F;
		fill = 64 - left;

		ctx->total[0] += (unsigned int)ilen;
		ctx->total[0] &= 0xFFFFFFFF;

		if (ctx->total[0] < ilen)
		{
    
    
			ctx->total[1]++;
		}

		if (left && ilen >= fill)
		{
    
    
			memcpy(ctx->buffer + left, input, fill);
			Process(ctx, ctx->buffer);
			input += fill;
			ilen -= fill;
			left = 0;
		}

		while (ilen >= 64)
		{
    
    
			Process(ctx, input);
			input += 64;
			ilen -= 64;
		}

		if (ilen > 0)
		{
    
    
			memcpy(ctx->buffer + left, input, ilen);
		}
	}

	static void Finish(sm3_context* ctx, unsigned char output[32])
	{
    
    
		unsigned char sm3_padding[64] = {
    
     0 };
		unsigned int last, padn;
		unsigned int high, low;
		unsigned char msglen[8];

		high = (ctx->total[0] >> 29)
			| (ctx->total[1] << 3);
		low = (ctx->total[0] << 3);

		PUT_UINT32_BE(high, msglen, 0);
		PUT_UINT32_BE(low, msglen, 4);

		last = ctx->total[0] & 0x3F;
		padn = (last < 56) ? (56 - last) : (120 - last);

		sm3_padding[0] = '\x80';
		Update(ctx, sm3_padding, padn);
		Update(ctx, msglen, 8);

		PUT_UINT32_BE(ctx->state[0], output, 0);
		PUT_UINT32_BE(ctx->state[1], output, 4);
		PUT_UINT32_BE(ctx->state[2], output, 8);
		PUT_UINT32_BE(ctx->state[3], output, 12);
		PUT_UINT32_BE(ctx->state[4], output, 16);
		PUT_UINT32_BE(ctx->state[5], output, 20);
		PUT_UINT32_BE(ctx->state[6], output, 24);
		PUT_UINT32_BE(ctx->state[7], output, 28);
	}

private:
	static void Process(sm3_context* ctx, unsigned char data[64])
	{
    
    
		unsigned int SS1, SS2, TT1, TT2, W[68], W1[64];
		unsigned int A, B, C, D, E, F, G, H;
		unsigned int T[64];
		unsigned int Temp1, Temp2, Temp3, Temp4, Temp5;
		unsigned int n;

		for (n = 0; n < 16; n++) {
    
     T[n] = 0x79CC4519; }
		for (n = 16; n < 64; n++) {
    
     T[n] = 0x7A879D8A; }

		GET_UINT32_BE(W[0], data, 0);
		GET_UINT32_BE(W[1], data, 4);
		GET_UINT32_BE(W[2], data, 8);
		GET_UINT32_BE(W[3], data, 12);
		GET_UINT32_BE(W[4], data, 16);
		GET_UINT32_BE(W[5], data, 20);
		GET_UINT32_BE(W[6], data, 24);
		GET_UINT32_BE(W[7], data, 28);
		GET_UINT32_BE(W[8], data, 32);
		GET_UINT32_BE(W[9], data, 36);
		GET_UINT32_BE(W[10], data, 40);
		GET_UINT32_BE(W[11], data, 44);
		GET_UINT32_BE(W[12], data, 48);
		GET_UINT32_BE(W[13], data, 52);
		GET_UINT32_BE(W[14], data, 56);
		GET_UINT32_BE(W[15], data, 60);

		for (n = 16; n < 68; n++)
		{
    
    
			Temp1 = W[n - 16] ^ W[n - 9];
			Temp2 = ROTL(W[n - 3], 15);
			Temp3 = Temp1 ^ Temp2;
			Temp4 = P1(Temp3);
			Temp5 = ROTL(W[n - 13], 7) ^ W[n - 6];
			W[n] = Temp4 ^ Temp5;
		}

		for (n = 0; n < 64; n++)
		{
    
    
			W1[n] = W[n] ^ W[n + 4];
		}

		A = ctx->state[0];
		B = ctx->state[1];
		C = ctx->state[2];
		D = ctx->state[3];
		E = ctx->state[4];
		F = ctx->state[5];
		G = ctx->state[6];
		H = ctx->state[7];

		for (n = 0; n < 16; n++)
		{
    
    
			SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[n], n)), 7);
			SS2 = SS1 ^ ROTL(A, 12);
			TT1 = FF0(A, B, C) + D + SS2 + W1[n];
			TT2 = GG0(E, F, G) + H + SS1 + W[n];
			D = C;
			C = ROTL(B, 9);
			B = A;
			A = TT1;
			H = G;
			G = ROTL(F, 19);
			F = E;
			E = P0(TT2);
		}

		for (n = 16; n < 64; n++)
		{
    
    
			SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[n], n)), 7);
			SS2 = SS1 ^ ROTL(A, 12);
			TT1 = FF1(A, B, C) + D + SS2 + W1[n];
			TT2 = GG1(E, F, G) + H + SS1 + W[n];
			D = C;
			C = ROTL(B, 9);
			B = A;
			A = TT1;
			H = G;
			G = ROTL(F, 19);
			F = E;
			E = P0(TT2);
		}

		ctx->state[0] ^= A;
		ctx->state[1] ^= B;
		ctx->state[2] ^= C;
		ctx->state[3] ^= D;
		ctx->state[4] ^= E;
		ctx->state[5] ^= F;
		ctx->state[6] ^= G;
		ctx->state[7] ^= H;
	}

	static void Process(unsigned char* input, size_t ilen, unsigned char output[32])
	{
    
    
		sm3_context ctx;
		Starts(&ctx);
		Update(&ctx, input, ilen);
		Finish(&ctx, output);
		memset(&ctx, 0, sizeof(sm3_context));
	}


	static void HmacStarts(sm3_context* ctx, unsigned char* key, size_t keylen)
	{
    
    
		unsigned int i;
		unsigned char sum[32];

		if (keylen > 64)
		{
    
    
			Process(key, keylen, sum);
			keylen = 32;
			key = sum;
		}

		memset(ctx->ipad, 0x36, 64);
		memset(ctx->opad, 0x5C, 64);

		for (i = 0; i < keylen; i++)
		{
    
    
			ctx->ipad[i] = (unsigned char)(ctx->ipad[i] ^ key[i]);
			ctx->opad[i] = (unsigned char)(ctx->opad[i] ^ key[i]);
		}

		Starts(ctx);
		Update(ctx, ctx->ipad, 64);

		memset(sum, 0, sizeof(sum));
	}

	static void HmacUpdate(sm3_context* ctx, unsigned char* input, size_t ilen)
	{
    
    
		Update(ctx, input, ilen);
	}

	static void HmacFinish(sm3_context* ctx, unsigned char output[32])
	{
    
    
		unsigned int hlen;
		unsigned char tmpbuf[32];

		hlen = 32;

		Finish(ctx, tmpbuf);
		Starts(ctx);
		Update(ctx, ctx->opad, 64);
		Update(ctx, tmpbuf, hlen);
		Finish(ctx, output);

		memset(tmpbuf, 0, sizeof(tmpbuf));
	}

	void Hmac(unsigned char* key, size_t keylen, unsigned char* input, size_t ilen, unsigned char output[32])
	{
    
    
		sm3_context ctx;

		HmacStarts(&ctx, key, keylen);
		HmacUpdate(&ctx, input, ilen);
		HmacFinish(&ctx, output);

		memset(&ctx, 0, sizeof(sm3_context));
	}
};

class SM2
{
    
    
public:
	int SM2Encrypt(const unsigned char* src, size_t srcLen, unsigned char* dst, size_t* dstLen)
	{
    
    
		const SM2_KEY* pk = &_key;
		if (encrypt(pk, src, srcLen, dst, dstLen) != 1) 
		{
    
    
			return 0;
		}
		return 1;
	}

	int SM2Decrypt(const unsigned char* src, size_t srcLen, unsigned char* dst, size_t* dstLen)
	{
    
    
		const SM2_KEY* sk = &_key;
		if (decrypt(sk, src, srcLen, dst, dstLen) != 1)
		{
    
    
			return 0;
		}
		return 1;
	}

	int SM2Sign(const unsigned char* src, size_t srcLen, unsigned char* sgn, size_t* sgnLen, bool rawRS = false)
	{
    
    
		const SM2_KEY* sk = &_key;
		sm3_context ctx;
		if (sign_init(&ctx, sk, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) != 1) 
		{
    
    
			return 0;
		}
		if (sign_update(&ctx, src, srcLen) != 1) 
		{
    
    
			return 0;
		}
		if (sign_finish(&ctx, sk, sgn, sgnLen, rawRS) != 1) 
		{
    
    
			return 0;
		}
		return 1;
	}

	int SM2Verify(const unsigned char* src, size_t srcLen, const unsigned char* sgn, size_t sgnLen, bool rawRS = false)
	{
    
    
		const SM2_KEY* sk = &_key;
		sm3_context ctx;
		if (verify_init(&ctx, sk, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) != 1)
		{
    
    
			return 0;
		}
		if (verify_update(&ctx, src, srcLen) != 1) 
		{
    
    
			return 0;
		}
		if (verify_finish(&ctx, sk, sgn, sgnLen, rawRS) != 1)
		{
    
    
			return 0;
		}
		return 1;
	}

	bool InitKey(const char* pri_key, const char* pub_key)
	{
    
    
		char pPubKey[256] = {
    
     0};
		char pPriKey[256] = {
    
     0 };
		if (pub_key && strlen(pub_key) == 130)
		{
    
    
			memcpy(pPubKey, pub_key + 2, 128);
		}
		if (pub_key && strlen(pub_key) == 128)
		{
    
    
			memcpy(pPubKey, pub_key, 128);
		}
		if (pri_key && strlen(pri_key) == 64)
		{
    
    
			memcpy(pPriKey, pri_key, 64);
		}
		if (pPubKey[0] == '\0' && pPriKey[0] == '\0')
		{
    
    
			return false;
		}
		memset(&_key, 0, sizeof(_key));

		// 初始化
		size_t nLen = 32;
		bool bRet = false;
		if (strlen(pPubKey) == 128)
		{
    
    
			Hex2Bin(pPubKey, 64, (char*)_key.public_key.x, nLen);
			Hex2Bin(pPubKey + 64, 64, (char*)_key.public_key.y, nLen);
			bRet = true;
		}
		if (strlen(pPriKey) == 64)
		{
    
    
			Hex2Bin(pPriKey, 64, (char*)_key.private_key, nLen);
			bRet = true;
		}
		return bRet;
	}


	static bool Bin2Hex(const char* pIn, size_t nIn, char* pOut, size_t& nOut)
	{
    
    
		if (pIn == nullptr || nIn == 0 || pOut == nullptr || nOut < 2 * nIn)
		{
    
    
			return false;
		}
		auto ps = (const unsigned char*)pIn;
		auto pe = ps + nIn;
		const char* hex = "0123456789abcdef";
		auto p = (unsigned char*)pOut;
		for (; ps < pe; ++ps, p += 2)
		{
    
    
			*p = hex[*ps >> 4];
			*(p + 1) = hex[*ps & 0xf];
		}
		nOut = 2 * nIn;
		return true;
	}

	static bool Hex2Bin(const char* pIn, size_t nIn, char* pOut, size_t& nOut)
	{
    
    
		if (pIn == nullptr || nIn == 0 || (nIn&1) || pOut == nullptr || nOut < nIn/2)
		{
    
    
			return false;
		}
		const unsigned char* ps = (const unsigned char*)pIn;
		const unsigned char* pe = ps + nIn;
		unsigned char* p = (unsigned char*)pOut;
		for (; ps + 1 < pe; ps += 2, ++p)
		{
    
    
			*p = (hex2int(*ps) << 4) | (hex2int(*(ps + 1)));
		}
		nOut = nIn / 2;
		return true;
	}

private:
	SM2_KEY _key;

private:
	static unsigned char hex2int(unsigned char c)
	{
    
    
		if (c >= 'a' && c <= 'f')
		{
    
    
			return (c - 'a' + 10);
		}
		else if (c >= 'A' && c <= 'F')
		{
    
    
			return (c - 'A' + 10);
		}
		else
		{
    
    
			return c - '0';
		}
	}

	void bn_to_bytes(const SM2_BN a, uint8_t out[32])
	{
    
    
		int i = 0;
		for (i = 7; i >= 0; i--) 
		{
    
    
			uint32_t ai = (uint32_t)a[i];
			PUTU32(out, ai);
			out += sizeof(uint32_t);
		}
	}

	void bn_from_bytes(SM2_BN r, const uint8_t in[32])
	{
    
    
		int i = 0;
		for (i = 7; i >= 0; i--) 
		{
    
    
			r[i] = GETU32(in);
			in += sizeof(uint32_t);
		}
	}

	void bn_set_word(SM2_BN r, uint32_t a)
	{
    
    
		int i = 0;
		r[0] = a;
		for (i = 1; i < 8; i++)
		{
    
    
			r[i] = 0;
		}
	}

	void jacobian_point_from_bytes(SM2_JACOBIAN_POINT* P, const uint8_t in[64])
	{
    
    
		bn_from_bytes(P->X, in);
		bn_from_bytes(P->Y, in + 32);
		bn_set_word(P->Z, 1);
	}

	int rand_bytes(uint8_t* buf, size_t len)
	{
    
    
		if (!len) 
		{
    
    
			return 0;
		}

#ifdef _WIN32
		/* Windows 实现 */
		NTSTATUS status = BCryptGenRandom(
			NULL,
			buf,
			(ULONG)len,
			BCRYPT_USE_SYSTEM_PREFERRED_RNG
		);
		return NT_SUCCESS(status) ? 1 : -1;
#else
		/* 类 Unix 实现 */
		int fd = open("/dev/urandom", O_RDONLY);
		if (fd == -1) return -1;

		ssize_t bytes_read = 0;
		while (bytes_read < (ssize_t)len) 
		{
    
    
			ssize_t n = read(fd, buf + bytes_read, len - bytes_read);
			if (n == -1) 
			{
    
    
				// 被中断信号打断则重试
				if (errno == EINTR) continue;
				close(fd);
				return -1;
			}
			// 永远不会触发(字符设备会持续提供数据)
			if (n == 0) 
			{
    
    
				close(fd);
				return -1;
			}
			bytes_read += n;
		}
		close(fd);
		return 1;
#endif
	}

	int bn_cmp(const SM2_BN a, const SM2_BN b)
	{
    
    
		int i;
		for (i = 7; i >= 0; i--) {
    
    
			if (a[i] > b[i])
				return 1;
			if (a[i] < b[i])
				return -1;
		}
		return 0;
	}

	int bn_rand_range(SM2_BN r, const SM2_BN range)
	{
    
    
		uint8_t buf[32];
		do
		{
    
    
			if (rand_bytes(buf, sizeof(buf)) != 1)
			{
    
    
				return -1;
			}
			bn_from_bytes(r, buf);
		} while (bn_cmp(r, range) >= 0);
		return 1;
	}

	int bn_is_zero(const SM2_BN a)
	{
    
    
		int i = 0;
		for (i = 0; i < 8; i++) 
		{
    
    
			if (a[i] != 0)
			{
    
    
				return 0;
			}
		}
		return 1;
	}

	int bn_is_one(const SM2_BN a)
	{
    
    
		int i;
		if (a[0] != 1)
			return 0;
		for (i = 1; i < 8; i++) {
    
    
			if (a[i] != 0)
				return 0;
		}
		return 1;
	}

	void bn_sub(SM2_BN ret, const SM2_BN a, const SM2_BN b)
	{
    
    
		int i;
		SM2_BN r;
		r[0] = ((uint64_t)1 << 32) + a[0] - b[0];
		for (i = 1; i < 7; i++) {
    
    
			r[i] = 0xffffffff + a[i] - b[i] + (r[i - 1] >> 32);
			r[i - 1] &= 0xffffffff;
		}
		r[i] = a[i] - b[i] + (r[i - 1] >> 32) - 1;
		r[i - 1] &= 0xffffffff;
		memcpy(ret, r, sizeof(SM2_BN));
	}

	int fn_rand(SM2_BN r)
	{
    
    
		if (bn_rand_range(r, SM2_N) != 1)
		{
    
    
			return -1;
		}
		return 1;
	}

	void fp_mul(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
	{
    
    
		int i = 0, j = 0;
		uint64_t s[16] = {
    
     0 };
		SM2_BN d = {
    
     0 };
		uint64_t u = 0;

		// s = a * b
		for (i = 0; i < 8; i++) 
		{
    
    
			u = 0;
			for (j = 0; j < 8; j++) 
			{
    
    
				u = s[i + j] + a[i] * b[j] + u;
				s[i + j] = u & 0xffffffff;
				u >>= 32;
			}
			s[i + 8] = u;
		}

		r[0] = s[0] + s[8] + s[9] + s[10] + s[11] + s[12] + ((s[13] + s[14] + s[15]) << 1);
		r[1] = s[1] + s[9] + s[10] + s[11] + s[12] + s[13] + ((s[14] + s[15]) << 1);
		r[2] = s[2];
		r[3] = s[3] + s[8] + s[11] + s[12] + s[14] + s[15] + (s[13] << 1);
		r[4] = s[4] + s[9] + s[12] + s[13] + s[15] + (s[14] << 1);
		r[5] = s[5] + s[10] + s[13] + s[14] + (s[15] << 1);
		r[6] = s[6] + s[11] + s[14] + s[15];
		r[7] = s[7] + s[8] + s[9] + s[10] + s[11] + s[15] + ((s[12] + s[13] + s[14] + s[15]) << 1);

		for (i = 1; i < 8; i++) 
		{
    
    
			r[i] += r[i - 1] >> 32;
			r[i - 1] &= 0xffffffff;
		}

		d[2] = s[8] + s[9] + s[13] + s[14];
		d[3] = d[2] >> 32;
		d[2] &= 0xffffffff;
		bn_sub(r, r, d);

		while (bn_cmp(r, SM2_P) >= 0) 
		{
    
    
			bn_sub(r, r, SM2_P);
		}
	}

	void fp_sqr(SM2_Fp r, const SM2_Fp a)
	{
    
    
		fp_mul(r, a, a);
	}

	void fp_inv(SM2_Fp r, const SM2_Fp a)
	{
    
    
		SM2_BN a1;
		SM2_BN a2;
		SM2_BN a3;
		SM2_BN a4;
		SM2_BN a5;
		int i;

		fp_sqr(a1, a);
		fp_mul(a2, a1, a);
		fp_sqr(a3, a2);
		fp_sqr(a3, a3);
		fp_mul(a3, a3, a2);
		fp_sqr(a4, a3);
		fp_sqr(a4, a4);
		fp_sqr(a4, a4);
		fp_sqr(a4, a4);
		fp_mul(a4, a4, a3);
		fp_sqr(a5, a4);
		for (i = 1; i < 8; i++) fp_sqr(a5, a5);
		fp_mul(a5, a5, a4);
		for (i = 0; i < 8; i++) fp_sqr(a5, a5);
		fp_mul(a5, a5, a4);
		for (i = 0; i < 4; i++) fp_sqr(a5, a5);
		fp_mul(a5, a5, a3);
		fp_sqr(a5, a5);
		fp_sqr(a5, a5);
		fp_mul(a5, a5, a2);
		fp_sqr(a5, a5);
		fp_mul(a5, a5, a);
		fp_sqr(a4, a5);
		fp_mul(a3, a4, a1);
		fp_sqr(a5, a4);
		for (i = 1; i < 31; i++) fp_sqr(a5, a5);
		fp_mul(a4, a5, a4);
		fp_sqr(a4, a4);
		fp_mul(a4, a4, a);
		fp_mul(a3, a4, a2);
		for (i = 0; i < 33; i++) fp_sqr(a5, a5);
		fp_mul(a2, a5, a3);
		fp_mul(a3, a2, a3);
		for (i = 0; i < 32; i++) fp_sqr(a5, a5);
		fp_mul(a2, a5, a3);
		fp_mul(a3, a2, a3);
		fp_mul(a4, a2, a4);
		for (i = 0; i < 32; i++) fp_sqr(a5, a5);
		fp_mul(a2, a5, a3);
		fp_mul(a3, a2, a3);
		fp_mul(a4, a2, a4);
		for (i = 0; i < 32; i++) fp_sqr(a5, a5);
		fp_mul(a2, a5, a3);
		fp_mul(a3, a2, a3);
		fp_mul(a4, a2, a4);
		for (i = 0; i < 32; i++) fp_sqr(a5, a5);
		fp_mul(a2, a5, a3);
		fp_mul(a3, a2, a3);
		fp_mul(a4, a2, a4);
		for (i = 0; i < 32; i++) fp_sqr(a5, a5);
		fp_mul(r, a4, a5);

		memset(a1, 0, sizeof(SM2_BN));
		memset(a2, 0, sizeof(SM2_BN));
		memset(a3, 0, sizeof(SM2_BN));
		memset(a4, 0, sizeof(SM2_BN));
		memset(a5, 0, sizeof(SM2_BN));
	}

	void bn_add(SM2_BN r, const SM2_BN a, const SM2_BN b)
	{
    
    
		int i = 0;
		r[0] = a[0] + b[0];

		for (i = 1; i < 8; i++)
		{
    
    
			r[i] = a[i] + b[i] + (r[i - 1] >> 32);
		}
		for (i = 0; i < 7; i++)
		{
    
    
			r[i] &= 0xffffffff;
		}
	}

	void fp_add(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
	{
    
    
		bn_add(r, a, b);
		if (bn_cmp(r, SM2_P) >= 0) 
		{
    
    
			bn_sub(r, r, SM2_P);
		}
	}

	void fp_sub(SM2_Fp r, const SM2_Fp a, const SM2_Fp b)
	{
    
    
		if (bn_cmp(a, b) >= 0) 
		{
    
    
			bn_sub(r, a, b);
		}
		else
		{
    
    
			SM2_BN t;
			bn_sub(t, SM2_P, b);
			bn_add(r, t, a);
		}
	}

	void fp_dbl(SM2_Fp r, const SM2_Fp a)
	{
    
    
		fp_add(r, a, a);
	}

	void fp_tri(SM2_Fp r, const SM2_Fp a)
	{
    
    
		SM2_BN t = {
    
     0 };
		fp_dbl(t, a);
		fp_add(r, t, a);
	}

	void fp_div2(SM2_Fp r, const SM2_Fp a)
	{
    
    
		int i = 0;
		memcpy(r, a, sizeof(SM2_BN));
		if (r[0] & 0x01)
		{
    
    
			bn_add(r, r, SM2_P);
		}
		for (i = 0; i < 7; i++) 
		{
    
    
			r[i] = (r[i] >> 1) | ((r[i + 1] & 0x01) << 31);
		}
		r[i] >>= 1;
	}

	void bn_to_bits(const SM2_BN a, char bits[256])
	{
    
    
		int i, j;
		uint64_t w;
		for (i = 7; i >= 0; i--)
		{
    
    
			w = a[i];
			for (j = 0; j < 32; j++)
			{
    
    
				*bits++ = (w & 0x80000000) ? '1' : '0';
				w <<= 1;
			}
		}
	}

	void fn_add(SM2_Fn r, const SM2_Fn a, const SM2_Fn b)
	{
    
    
		bn_add(r, a, b);
		if (bn_cmp(r, SM2_N) >= 0) 
		{
    
    
			bn_sub(r, r, SM2_N);
		}
	}

	void fn_sub(SM2_Fn r, const SM2_Fn a, const SM2_Fn b)
	{
    
    
		if (bn_cmp(a, b) >= 0) 
		{
    
    
			bn_sub(r, a, b);
		}
		else 
		{
    
    
			SM2_BN t;
			bn_add(t, a, SM2_N);
			bn_sub(r, t, b);
		}
	}

	/* bn288 only used in barrett reduction */
	static int bn288_cmp(const uint64_t a[9], const uint64_t b[9])
	{
    
    
		int i = 0;
		for (i = 8; i >= 0; i--) 
		{
    
    
			if (a[i] > b[i]) return 1;
			if (a[i] < b[i]) return -1;
		}
		return 0;
	}

	static void bn288_add(uint64_t r[9], const uint64_t a[9], const uint64_t b[9])
	{
    
    
		int i = 0;
		r[0] = a[0] + b[0];
		for (i = 1; i < 9; i++) 
		{
    
    
			r[i] = a[i] + b[i] + (r[i - 1] >> 32);
		}
		for (i = 0; i < 8; i++) 
		{
    
    
			r[i] &= 0xffffffff;
		}
	}

	static void bn288_sub(uint64_t ret[9], const uint64_t a[9], const uint64_t b[9])
	{
    
    
		int i = 0;
		uint64_t r[9] = {
    
     0 };

		r[0] = ((uint64_t)1 << 32) + a[0] - b[0];
		for (i = 1; i < 8; i++) 
		{
    
    
			r[i] = 0xffffffff + a[i] - b[i] + (r[i - 1] >> 32);
			r[i - 1] &= 0xffffffff;
		}
		r[i] = a[i] - b[i] + (r[i - 1] >> 32) - 1;
		r[i - 1] &= 0xffffffff;

		for (i = 0; i < 9; i++) 
		{
    
    
			ret[i] = r[i];
		}
	}

	void fn_mul(SM2_BN ret, const SM2_BN a, const SM2_BN b)
	{
    
    
		SM2_BN r;
		static const uint64_t mu[9] = 
		{
    
    
			0xf15149a0, 0x12ac6361, 0xfa323c01, 0x8dfc2096, 1, 1, 1, 1, 1,
		};

		uint64_t s[18] = {
    
     0 };
		uint64_t zh[9] = {
    
     0 };
		uint64_t zl[9] = {
    
     0 };
		uint64_t q[9] = {
    
     0 };
		uint64_t w = 0;
		int i = 0, j = 0;

		/* z = a * b */
		for (i = 0; i < 8; i++) 
		{
    
    
			s[i] = 0;
		}
		for (i = 0; i < 8; i++) 
		{
    
    
			w = 0;
			for (j = 0; j < 8; j++) 
			{
    
    
				w += s[i + j] + a[i] * b[j];
				s[i + j] = w & 0xffffffff;
				w >>= 32;
			}
			s[i + 8] = w;
		}

		/* zl = z mod (2^32)^9 = z[0..8]
		 * zh = z // (2^32)^7 = z[7..15] */
		for (i = 0; i < 9; i++)
		{
    
    
			zl[i] = s[i];
			zh[i] = s[7 + i];
		}

		/* q = zh * mu // (2^32)^9 */
		for (i = 0; i < 9; i++) 
		{
    
    
			s[i] = 0;
		}
		for (i = 0; i < 9; i++) 
		{
    
    
			w = 0;
			for (j = 0; j < 9; j++) 
			{
    
    
				w += s[i + j] + zh[i] * mu[j];
				s[i + j] = w & 0xffffffff;
				w >>= 32;
			}
			s[i + 9] = w;
		}
		for (i = 0; i < 8; i++) 
		{
    
    
			q[i] = s[9 + i];
		}

		/* q = q * n mod (2^32)^9 */
		for (i = 0; i < 17; i++) 
		{
    
    
			s[i] = 0;
		}
		for (i = 0; i < 8; i++) 
		{
    
    
			w = 0;
			for (j = 0; j < 8; j++)
			{
    
    
				w += s[i + j] + q[i] * SM2_N[j];
				s[i + j] = w & 0xffffffff;
				w >>= 32;
			}
			s[i + 8] = w;
		}
		for (i = 0; i < 9; i++) 
		{
    
    
			q[i] = s[i];
		}

		/* r = zl - q (mod (2^32)^9) */
		if (bn288_cmp(zl, q)) 
		{
    
    
			bn288_sub(zl, zl, q);
		}
		else 
		{
    
    
			uint64_t c[9] = {
    
     0,0,0,0,0,0,0,0,0x100000000 };
			bn288_sub(q, c, q);
			bn288_add(zl, q, zl);
		}
		for (i = 0; i < 8; i++) 
		{
    
    
			r[i] = zl[i];
		}
		r[7] += zl[8] << 32;

		/* while r >= p do: r = r - n */
		while (bn_cmp(r, SM2_N) >= 0)
		{
    
    
			bn_sub(r, r, SM2_N);
		}
		memcpy(ret, r, sizeof(SM2_BN));
	}

	void fn_sqr(SM2_BN r, const SM2_BN a)
	{
    
    
		fn_mul(r, a, a);
	}

	void fn_exp(SM2_BN r, const SM2_BN a, const SM2_BN e)
	{
    
    
		SM2_BN t = {
    
     0 };
		uint32_t w = 0;
		int i = 0, j = 0;

		bn_set_word(t, 1);
		for (i = 7; i >= 0; i--) 
		{
    
    
			w = (uint32_t)e[i];
			for (j = 0; j < 32; j++) 
			{
    
    
				fn_sqr(t, t);
				if (w & 0x80000000) 
				{
    
    
					fn_mul(t, t, a);
				}
				w <<= 1;
			}
		}
		memcpy(r, t, sizeof(SM2_BN));
	}

	void fn_inv(SM2_BN r, const SM2_BN a)
	{
    
    
		SM2_BN e = {
    
     0 };
		bn_sub(e, SM2_N, SM2_TWO);
		fn_exp(r, a, e);
	}

	void jacobian_point_set_xy(SM2_JACOBIAN_POINT* R, const SM2_BN x, const SM2_BN y)
	{
    
    
		memcpy(R->X, x, sizeof(SM2_BN));
		memcpy(R->Y, y, sizeof(SM2_BN));
		bn_set_word(R->Z, 1);
	}

	void jacobian_point_get_xy(const SM2_JACOBIAN_POINT* P, SM2_BN x, SM2_BN y)
	{
    
    
		if (bn_is_one(P->Z)) 
		{
    
    
			memcpy(x, P->X, sizeof(SM2_BN));
			if (y) 
			{
    
    
				memcpy(y, P->Y, sizeof(SM2_BN));
			}
		}
		else 
		{
    
    
			SM2_BN z_inv;
			fp_inv(z_inv, P->Z);
			if (y) 
			{
    
    
				fp_mul(y, P->Y, z_inv);
			}
			fp_sqr(z_inv, z_inv);
			fp_mul(x, P->X, z_inv);
			if (y) 
			{
    
    
				fp_mul(y, y, z_inv);
			}
		}
	}

	void jacobian_point_init(SM2_JACOBIAN_POINT* R)
	{
    
    
		memset(R, 0, sizeof(SM2_JACOBIAN_POINT));
		R->X[0] = 1;
		R->Y[0] = 1;
	}

	int jacobian_point_is_on_curve(const SM2_JACOBIAN_POINT* P)
	{
    
    
		SM2_BN t0 = {
    
     0 };
		SM2_BN t1 = {
    
     0 };
		SM2_BN t2 = {
    
     0 };

		if (bn_is_one(P->Z)) 
		{
    
    
			fp_sqr(t0, P->Y);
			fp_add(t0, t0, P->X);
			fp_add(t0, t0, P->X);
			fp_add(t0, t0, P->X);
			fp_sqr(t1, P->X);
			fp_mul(t1, t1, P->X);
			fp_add(t1, t1, SM2_B);
		}
		else 
		{
    
    
			fp_sqr(t0, P->Y);
			fp_sqr(t1, P->Z);
			fp_sqr(t2, t1);
			fp_mul(t1, t1, t2);
			fp_mul(t1, t1, SM2_B);
			fp_mul(t2, t2, P->X);
			fp_add(t0, t0, t2);
			fp_add(t0, t0, t2);
			fp_add(t0, t0, t2);
			fp_sqr(t2, P->X);
			fp_mul(t2, t2, P->X);
			fp_add(t1, t1, t2);
		}

		if (bn_cmp(t0, t1) != 0) 
		{
    
    
			return -1;
		}
		return 1;
	}

	void jacobian_point_dbl(SM2_JACOBIAN_POINT* R, const SM2_JACOBIAN_POINT* P)
	{
    
    
		const uint64_t* X1 = P->X;
		const uint64_t* Y1 = P->Y;
		const uint64_t* Z1 = P->Z;
		SM2_BN T1 = {
    
     0 };
		SM2_BN T2 = {
    
     0 };
		SM2_BN T3 = {
    
     0 };
		SM2_BN X3 = {
    
     0 };
		SM2_BN Y3 = {
    
     0 };
		SM2_BN Z3 = {
    
     0 };

		if (bn_is_zero(P->Z)) 
		{
    
    
			memcpy(R, P, sizeof(SM2_JACOBIAN_POINT));
			return;
		}

		fp_sqr(T1, Z1);
		fp_sub(T2, X1, T1);
		fp_add(T1, X1, T1);
		fp_mul(T2, T2, T1);
		fp_tri(T2, T2);
		fp_dbl(Y3, Y1);
		fp_mul(Z3, Y3, Z1);
		fp_sqr(Y3, Y3);
		fp_mul(T3, Y3, X1);
		fp_sqr(Y3, Y3);
		fp_div2(Y3, Y3);
		fp_sqr(X3, T2);
		fp_dbl(T1, T3);
		fp_sub(X3, X3, T1);
		fp_sub(T1, T3, X3);
		fp_mul(T1, T1, T2);
		fp_sub(Y3, T1, Y3);

		memcpy(R->X, X3, sizeof(SM2_BN));
		memcpy(R->Y, Y3, sizeof(SM2_BN));
		memcpy(R->Z, Z3, sizeof(SM2_BN));
	}

	void jacobian_point_add(SM2_JACOBIAN_POINT* R, const SM2_JACOBIAN_POINT* P, const SM2_JACOBIAN_POINT* Q)
	{
    
    
		const uint64_t* X1 = P->X;
		const uint64_t* Y1 = P->Y;
		const uint64_t* Z1 = P->Z;
		const uint64_t* x2 = Q->X;
		const uint64_t* y2 = Q->Y;
		SM2_BN T1 = {
    
     0 };
		SM2_BN T2 = {
    
     0 };
		SM2_BN T3 = {
    
     0 };
		SM2_BN T4 = {
    
     0 };
		SM2_BN X3 = {
    
     0 };
		SM2_BN Y3 = {
    
     0 };
		SM2_BN Z3 = {
    
     0 };

		if (bn_is_zero(Q->Z)) 
		{
    
    
			memcpy(R, P, sizeof(SM2_JACOBIAN_POINT));
			return;
		}

		if (bn_is_zero(P->Z)) 
		{
    
    
			memcpy(R, Q, sizeof(SM2_JACOBIAN_POINT));
			return;
		}

		if (bn_is_one(Q->Z) == 0) 
		{
    
    
			return;
		}

		fp_sqr(T1, Z1);
		fp_mul(T2, T1, Z1);
		fp_mul(T1, T1, x2);
		fp_mul(T2, T2, y2);
		fp_sub(T1, T1, X1);
		fp_sub(T2, T2, Y1);
		if (bn_is_zero(T1)) 
		{
    
    
			if (bn_is_zero(T2)) 
			{
    
    
				SM2_JACOBIAN_POINT _Q, * Q = &_Q;
				jacobian_point_set_xy(Q, x2, y2);

				jacobian_point_dbl(R, Q);
				return;
			}
			else 
			{
    
    
				jacobian_point_init(R);
				return;
			}
		}
		fp_mul(Z3, Z1, T1);
		fp_sqr(T3, T1);
		fp_mul(T4, T3, T1);
		fp_mul(T3, T3, X1);
		fp_dbl(T1, T3);
		fp_sqr(X3, T2);
		fp_sub(X3, X3, T1);
		fp_sub(X3, X3, T4);
		fp_sub(T3, T3, X3);
		fp_mul(T3, T3, T2);
		fp_mul(T4, T4, Y1);
		fp_sub(Y3, T3, T4);

		memcpy(R->X, X3, sizeof(SM2_BN));
		memcpy(R->Y, Y3, sizeof(SM2_BN));
		memcpy(R->Z, Z3, sizeof(SM2_BN));
	}

	void jacobian_point_mul(SM2_JACOBIAN_POINT* R, const SM2_BN k, const SM2_JACOBIAN_POINT* P)
	{
    
    
		char bits[257] = {
    
     0 };
		SM2_JACOBIAN_POINT _Q, * Q = &_Q;
		SM2_JACOBIAN_POINT _T, * T = &_T;
		int i;

		if (!bn_is_one(P->Z)) 
		{
    
    
			SM2_BN x = {
    
     0 };
			SM2_BN y = {
    
     0 };
			jacobian_point_get_xy(P, x, y);
			jacobian_point_set_xy(T, x, y);
			P = T;
		}

		jacobian_point_init(Q);
		bn_to_bits(k, bits);
		for (i = 0; i < 256; i++) 
		{
    
    
			jacobian_point_dbl(Q, Q);
			if (bits[i] == '1') 
			{
    
    
				jacobian_point_add(Q, Q, P);
			}
		}
		memcpy(R, Q, sizeof(SM2_JACOBIAN_POINT));
	}

	void jacobian_point_to_bytes(const SM2_JACOBIAN_POINT* P, uint8_t out[64])
	{
    
    
		SM2_BN x = {
    
     0 };
		SM2_BN y = {
    
     0 };
		jacobian_point_get_xy(P, x, y);
		bn_to_bytes(x, out);
		bn_to_bytes(y, out + 32);
	}

	void jacobian_point_mul_generator(SM2_JACOBIAN_POINT* R, const SM2_BN k)
	{
    
    
		jacobian_point_mul(R, k, SM2_G);
	}

	/* R = t * P + s * G */
	void jacobian_point_mul_sum(SM2_JACOBIAN_POINT* R, const SM2_BN t, const SM2_JACOBIAN_POINT* P, const SM2_BN s)
	{
    
    
		SM2_JACOBIAN_POINT _sG, * sG = &_sG;
		SM2_BN x = {
    
     0 };
		SM2_BN y = {
    
     0 };

		/* T = s * G */
		jacobian_point_mul_generator(sG, s);

		// R = t * P
		jacobian_point_mul(R, t, P);
		jacobian_point_get_xy(R, x, y);
		jacobian_point_set_xy(R, x, y);

		// R = R + T
		jacobian_point_add(R, sG, R);
	}

	int sm2_point_is_on_curve(const SM2_POINT* P)
	{
    
    
		SM2_JACOBIAN_POINT T;
		jacobian_point_from_bytes(&T, (const uint8_t*)P);
		return jacobian_point_is_on_curve(&T);
	}

	int kdf(const uint8_t* in, size_t inlen, size_t outlen, uint8_t* out)
	{
    
    
		sm3_context ctx;
		uint8_t counter_be[4];
		uint8_t dgst[32];
		uint32_t counter = 1;
		size_t len;

		while (outlen) {
    
    
			PUTU32(counter_be, counter);
			counter++;

			SM3::Starts(&ctx);
			SM3::Update(&ctx, (uint8_t*)in, (int)inlen);
			SM3::Update(&ctx, counter_be, sizeof(counter_be));
			SM3::Finish(&ctx, dgst);

			len = outlen < 32 ? outlen : 32;
			memcpy(out, dgst, len);
			out += len;
			outlen -= len;
		}

		memset(&ctx, 0, sizeof(sm3_context));
		memset(dgst, 0, sizeof(dgst));
		return 1;
	}

	static int all_zero(const uint8_t* buf, size_t len)
	{
    
    
		size_t i = 0;
		for (i = 0; i < len; i++) 
		{
    
    
			if (buf[i]) 
			{
    
    
				return 0;
			}
		}
		return 1;
	}

	void gm_memxor(void* r, const void* a, const void* b, size_t len)
	{
    
    
		uint8_t* pr = (uint8_t*)r;
		const uint8_t* pa = (const uint8_t*)a;
		const uint8_t* pb = (const uint8_t*)b;
		size_t i;
		for (i = 0; i < len; i++) 
		{
    
    
			pr[i] = pa[i] ^ pb[i];
		}
	}

	int asn1_length_to_der(size_t len, uint8_t** out, size_t* outlen)
	{
    
    
		if (len > INT_MAX) 
		{
    
    
			return -1;
		}
		if (!outlen) 
		{
    
    
			return -1;
		}

		if (len < 128) 
		{
    
    
			if (out && *out) 
			{
    
    
				*(*out)++ = (uint8_t)len;
			}
			(*outlen)++;

		}
		else 
		{
    
    
			uint8_t buf[4] = {
    
     0 };
			int nbytes = 0;

			if (len < 256) nbytes = 1;
			else if (len < 65536) nbytes = 2;
			else if (len < (1 << 24)) nbytes = 3;
			else nbytes = 4;
			PUTU32(buf, (uint32_t)len);

			if (out && *out) 
			{
    
    
				*(*out)++ = 0x80 + nbytes;
				memcpy(*out, buf + 4 - nbytes, nbytes);
				(*out) += nbytes;
			}
			(*outlen) += 1 + nbytes;
		}
		return 1;
	}

	int asn1_length_from_der(size_t* len, const uint8_t** in, size_t* inlen)
	{
    
    
		if (!len || !in || !(*in) || !inlen) 
		{
    
    
			return -1;
		}

		if (*inlen == 0) 
		{
    
    
			return -1;
		}

		if (**in < 128) 
		{
    
    
			*len = *(*in)++;
			(*inlen)--;

		}
		else 
		{
    
    
			uint8_t buf[4] = {
    
     0 };
			size_t nbytes = *(*in)++ & 0x7f;
			(*inlen)--;

			if (nbytes < 1 || nbytes > 4) 
			{
    
    
				return -1;
			}
			if (*inlen < nbytes) 
			{
    
    
				return -1;
			}

			memcpy(buf + 4 - nbytes, *in, nbytes);
			*len = (size_t)GETU32(buf);
			*in += nbytes;
			*inlen -= nbytes;
		}

		// check if the left input is enough for reading
		if (*inlen < *len) 
		{
    
    
			return -2;
		}
		return 1;
	}

	int asn1_integer_to_der_ex(int tag, const uint8_t* a, size_t alen, uint8_t** out, size_t* outlen)
	{
    
    
		if (!outlen) 
		{
    
    
			return -1;
		}

		if (!a) 
		{
    
    
			return 0;
		}
		if (alen <= 0 || alen > INT_MAX) 
		{
    
    
			return -1;
		}

		if (out && *out) 
		{
    
    
			*(*out)++ = tag;
		}
		(*outlen)++;

		while (*a == 0 && alen > 1) 
		{
    
    
			a++;
			alen--;
		}

		if (a[0] & 0x80) 
		{
    
    
			asn1_length_to_der(alen + 1, out, outlen);
			if (out && *out) 
			{
    
    
				*(*out)++ = 0x00;
				memcpy(*out, a, alen);
				(*out) += alen;
			}
			(*outlen) += 1 + alen;
		}
		else 
		{
    
    
			asn1_length_to_der(alen, out, outlen);
			if (out && *out) 
			{
    
    
				memcpy(*out, a, alen);
				(*out) += alen;
			}
			(*outlen) += alen;
		}

		return 1;
	}

	//02200000FD0A697866AA1C79237AB65F740E24D9098C0846710EF692E187AA740BEF
	//022000009286DCB4F8183AE098FB3817749F46FE15B9822AC20483D4BD037590BA6C
	//022100917A0B39FBF089EC08D291A44E8171960F80BE6346A1AC97BCDD0642C69FE801
	int asn1_integer_from_der_32(int tag, const uint8_t** a, size_t* alen, const uint8_t** in, size_t* inlen)
	{
    
    
		size_t len = 0;

		if (!a || !alen || !in || !(*in) || !inlen)
		{
    
    
			return -1;
		}

		// tag
		if (*inlen == 0 || **in != tag) 
		{
    
    
			*a = NULL;
			*alen = 0;
			return 0;
		}
		(*in)++;
		(*inlen)--;

		// length (not zero)
		if (asn1_length_from_der(&len, in, inlen) != 1) 
		{
    
    
			return -1;
		}
		if (len == 0) 
		{
    
    
			return -1;
		}

		// check if ASN1_INTEGER is negative
		if (**in & 0x80) 
		{
    
    
			return -1;
		}

		// remove leading zero
		if (**in == 0 && len == 33) 
		{
    
    
			(*in)++;
			(*inlen)--;
			len--;
		}

		// return integer bytes
		*a = *in;
		*alen = len;
		*in += len;
		*inlen -= len;

		return 1;
	}

	int asn1_header_to_der(int tag, size_t dlen, uint8_t** out, size_t* outlen)
	{
    
    
		if (!outlen) 
		{
    
    
			return -1;
		}

		if (out && *out) 
		{
    
    
			*(*out)++ = (uint8_t)tag;
		}
		(*outlen)++;

		(void)asn1_length_to_der(dlen, out, outlen);
		return 1;
	}

	int asn1_type_to_der(int tag, const uint8_t* d, size_t dlen, uint8_t** out, size_t* outlen)
	{
    
    
		if (!outlen) 
		{
    
    
			return -1;
		}

		if (!d) 
		{
    
    
			if (dlen) 
			{
    
    
				return -1;
			}
			return 0;
		}

		// tag
		if (out && *out) 
		{
    
    
			*(*out)++ = (uint8_t)tag;
		}
		(*outlen)++;

		// length
		(void)asn1_length_to_der(dlen, out, outlen);

		// data
		if (out && *out) 
		{
    
    
			memcpy(*out, d, dlen);
			*out += dlen;
		}
		*outlen += dlen;

		return 1;
	}

	int asn1_type_from_der(int tag, const uint8_t** d, size_t* dlen, const uint8_t** in, size_t* inlen)
	{
    
    
		if (!d || !dlen || !in || !(*in) || !inlen) 
		{
    
    
			return -1;
		}

		// tag
		if (*inlen == 0 || **in != tag) 
		{
    
    
			*d = NULL;
			*dlen = 0;
			return 0;
		}
		(*in)++;
		(*inlen)--;

		// length
		if (asn1_length_from_der(dlen, in, inlen) != 1) 
		{
    
    
			return -1;
		}

		// data
		*d = *in;
		*in += *dlen;
		*inlen -= *dlen;
		return 1;
	}

	int asn1_check(int expr)
	{
    
    
		if (expr) 
		{
    
    
			return 1;
		}
		return -1;
	}

	int asn1_length_is_zero(size_t len)
	{
    
    
		if (len) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int asn1_length_le(size_t len1, size_t len2)
	{
    
    
		if (len1 > len2) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int ciphertext_to_der(const SM2_CIPHERTEXT* C, uint8_t** out, size_t* outlen)
	{
    
    
		size_t len = 0;
		if (!C) 
		{
    
    
			return 0;
		}
		if (asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.x, 32, NULL, &len) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.y, 32, NULL, &len) != 1
			|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->hash, 32, NULL, &len) != 1
			|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->ciphertext, C->ciphertext_size, NULL, &len) != 1
			|| asn1_header_to_der(ASN1_TAG_SEQUENCE, len, out, outlen) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.x, 32, out, outlen) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, C->point.y, 32, out, outlen) != 1
			|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->hash, 32, out, outlen) != 1
			|| asn1_type_to_der(ASN1_TAG_OCTET_STRING, C->ciphertext, C->ciphertext_size, out, outlen) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int ciphertext_from_der(SM2_CIPHERTEXT* C, const uint8_t** in, size_t* inlen)
	{
    
    
		int ret = 0;
		const uint8_t* d = nullptr;
		size_t dlen = 0;
		const uint8_t* x = nullptr;
		const uint8_t* y = nullptr;
		const uint8_t* hash = nullptr;
		const uint8_t* c = nullptr;
		size_t xlen = 0, ylen = 0, hashlen = 0, clen = 0;

		if ((ret = asn1_type_from_der(ASN1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1) 
		{
    
    
			return ret;
		}
		if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &x, &xlen, &d, &dlen) != 1
			|| asn1_length_le(xlen, 32) != 1) 
		{
    
    
			return -1;
		}
		if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &y, &ylen, &d, &dlen) != 1
			|| asn1_length_le(ylen, 32) != 1) 
		{
    
    
			return -1;
		}
		if (asn1_type_from_der(ASN1_TAG_OCTET_STRING, &hash, &hashlen, &d, &dlen) != 1
			|| asn1_check(hashlen == 32) != 1) 
		{
    
    
			return -1;
		}
		if (asn1_type_from_der(ASN1_TAG_OCTET_STRING, &c, &clen, &d, &dlen) != 1
			|| asn1_length_le(clen, SM2_MAX_PLAINTEXT_SIZE) != 1) 
		{
    
    
			return -1;
		}
		if (asn1_length_is_zero(dlen) != 1) 
		{
    
    
			return -1;
		}
		memset(C, 0, sizeof(SM2_CIPHERTEXT));
		memcpy(C->point.x + 32 - xlen, x, xlen);
		memcpy(C->point.y + 32 - ylen, y, ylen);
		if (sm2_point_is_on_curve(&C->point) != 1) 
		{
    
    
			return -1;
		}
		memcpy(C->hash, hash, hashlen);
		memcpy(C->ciphertext, c, clen);
		C->ciphertext_size = (uint32_t)clen;
		return 1;
	}

	int signature_to_der(const SM2_SIGNATURE* sig, uint8_t** out, size_t* outlen)
	{
    
    
		size_t len = 0;
		if (!sig) 
		{
    
    
			return 0;
		}
		if (asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->r, 32, NULL, &len) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->s, 32, NULL, &len) != 1
			|| asn1_header_to_der(ASN1_TAG_SEQUENCE, len, out, outlen) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->r, 32, out, outlen) != 1
			|| asn1_integer_to_der_ex(ASN1_TAG_INTEGER, sig->s, 32, out, outlen) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int signature_from_der(SM2_SIGNATURE* sig, const uint8_t** in, size_t* inlen)
	{
    
    
		int ret = 0;
		const uint8_t* d = nullptr;
		size_t dlen = 0;
		const uint8_t* r = nullptr;
		size_t rlen = 0;
		const uint8_t* s = nullptr;
		size_t slen = 0;

		if ((ret = asn1_type_from_der(ASN1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1) 
		{
    
    
			return ret;
		}
		if (asn1_integer_from_der_32(ASN1_TAG_INTEGER, &r, &rlen, &d, &dlen) != 1
			|| asn1_integer_from_der_32(ASN1_TAG_INTEGER, &s, &slen, &d, &dlen) != 1
			|| asn1_length_le(rlen, 32) != 1
			|| asn1_length_le(slen, 32) != 1
			|| asn1_length_is_zero(dlen) != 1) 
		{
    
    
			return -1;
		}
		memset(sig, 0, sizeof(*sig));
		memcpy(sig->r + 32 - rlen, r, rlen);
		memcpy(sig->s + 32 - slen, s, slen);
		return 1;
	}

	int do_encrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, SM2_CIPHERTEXT* out)
	{
    
    
		SM2_BN k = {
    
     0 };
		SM2_JACOBIAN_POINT _P, * P = &_P;
		SM2_JACOBIAN_POINT _C1, * C1 = &_C1;
		SM2_JACOBIAN_POINT _kP, * kP = &_kP;
		uint8_t x2y2[64] = {
    
     0 };
		sm3_context sm3_ctx;

		if (!(SM2_MIN_PLAINTEXT_SIZE <= inlen && inlen <= SM2_MAX_PLAINTEXT_SIZE)) 
		{
    
    
			return -1;
		}

		jacobian_point_from_bytes(P, (uint8_t*)&key->public_key);

		// S = h * P, check S != O
		// for sm2 curve, h == 1 and S == P
		// SM2_POINT can not present point at infinity, do do nothing here

	retry:
		// rand k in [1, n - 1]
		do {
    
    
			if (fn_rand(k) != 1) 
			{
    
    
				return -1;
			}
		} while (bn_is_zero(k));

		// output C1 = k * G = (x1, y1)
		jacobian_point_mul_generator(C1, k);
		jacobian_point_to_bytes(C1, (uint8_t*)&out->point);

		// k * P = (x2, y2)
		jacobian_point_mul(kP, k, P);
		jacobian_point_to_bytes(kP, x2y2);

		// t = KDF(x2 || y2, inlen)
		kdf(x2y2, 64, inlen, out->ciphertext);

		// if t is all zero, retry
		if (all_zero(out->ciphertext, inlen)) 
		{
    
    
			goto retry;
		}

		// output C2 = M xor t
		gm_memxor(out->ciphertext, out->ciphertext, in, inlen);
		out->ciphertext_size = (uint32_t)inlen;

		// output C3 = Hash(x2 || m || y2)
		SM3::Starts(&sm3_ctx);
		SM3::Update(&sm3_ctx, x2y2, 32);
		SM3::Update(&sm3_ctx, (uint8_t*)in, inlen);
		SM3::Update(&sm3_ctx, x2y2 + 32, 32);
		SM3::Finish(&sm3_ctx, out->hash);

		memset(k, 0, sizeof(k));
		memset(kP, 0, sizeof(SM2_JACOBIAN_POINT));
		memset(x2y2, 0, sizeof(x2y2));
		return 1;
	}

	int do_decrypt(const SM2_KEY* key, const SM2_CIPHERTEXT* in, uint8_t* out, size_t* outlen)
	{
    
    
		int ret = -1;
		SM2_BN d;
		SM2_JACOBIAN_POINT _C1, * C1 = &_C1;
		uint8_t x2y2[64];
		sm3_context sm3_ctx;
		uint8_t hash[32];

		// check C1 is on sm2 curve
		jacobian_point_from_bytes(C1, (uint8_t*)&in->point);
		if (!jacobian_point_is_on_curve(C1)) 
		{
    
    
			return -1;
		}

		// check if S = h * C1 is point at infinity
		// this will not happen, as SM2_POINT can not present point at infinity

		// d * C1 = (x2, y2)
		bn_from_bytes(d, key->private_key);
		jacobian_point_mul(C1, d, C1);

		// t = KDF(x2 || y2, klen) and check t is not all zeros
		jacobian_point_to_bytes(C1, x2y2);
		kdf(x2y2, 64, in->ciphertext_size, out);
		if (all_zero(out, in->ciphertext_size)) 
		{
    
    
			goto end;
		}

		// M = C2 xor t
		gm_memxor(out, out, in->ciphertext, in->ciphertext_size);
		*outlen = in->ciphertext_size;

		// u = Hash(x2 || M || y2)
		SM3::Starts(&sm3_ctx);
		SM3::Update(&sm3_ctx, x2y2, 32);
		SM3::Update(&sm3_ctx, out, in->ciphertext_size);
		SM3::Update(&sm3_ctx, x2y2 + 32, 32);
		SM3::Finish(&sm3_ctx, hash);

		// check if u == C3
		if (memcmp(in->hash, hash, sizeof(hash)) != 0) 
		{
    
    
			goto end;
		}
		ret = 1;

	end:
		memset(d, 0, sizeof(d));
		memset(C1, 0, sizeof(SM2_JACOBIAN_POINT));
		memset(x2y2, 0, sizeof(x2y2));
		return ret;
	}

	int encrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen)
	{
    
    
		SM2_CIPHERTEXT C;

		if (!key || !in || !out || !outlen) 
		{
    
    
			return -1;
		}
		if (!inlen) 
		{
    
    
			return -1;
		}

		if (do_encrypt(key, in, inlen, &C) != 1) 
		{
    
    
			return -1;
		}
		*outlen = 0;
		if (ciphertext_to_der(&C, &out, outlen) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int decrypt(const SM2_KEY* key, const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen)
	{
    
    
		SM2_CIPHERTEXT C;

		if (!key || !in || !out || !outlen) 
		{
    
    
			return -1;
		}
		if (ciphertext_from_der(&C, &in, &inlen) != 1
			|| asn1_length_is_zero(inlen) != 1) 
		{
    
    
			return -1;
		}
		if (do_decrypt(key, &C, out, outlen) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int sm2_do_sign(const SM2_KEY* key, const uint8_t dgst[32], SM2_SIGNATURE* sig)
	{
    
    
		SM2_JACOBIAN_POINT _P, * P = &_P;
		SM2_BN d;
		SM2_BN d_inv;
		SM2_BN e;
		SM2_BN k;
		SM2_BN x;
		SM2_BN t;
		SM2_BN r;
		SM2_BN s;

		bn_from_bytes(d, key->private_key);

		// compute (d + 1)^-1 (mod n)
		fn_add(d_inv, d, SM2_ONE);
		if (bn_is_zero(d_inv)) 
		{
    
    
			return -1;
		}
		fn_inv(d_inv, d_inv);

		// e = H(M)
		bn_from_bytes(e, dgst);

	retry:
		// rand k in [1, n - 1]
		do {
    
    
			if (fn_rand(k) != 1) 
			{
    
    
				return -1;
			}
		} while (bn_is_zero(k));

		// (x, y) = kG
		jacobian_point_mul_generator(P, k);
		jacobian_point_get_xy(P, x, NULL);

		// r = e + x (mod n)
		if (bn_cmp(e, SM2_N) >= 0) 
		{
    
    
			bn_sub(e, e, SM2_N);
		}
		if (bn_cmp(x, SM2_N) >= 0) {
    
    
			bn_sub(x, x, SM2_N);
		}
		fn_add(r, e, x);

		// if r == 0 or r + k == n re-generate k
		bn_add(t, r, k);
		if (bn_is_zero(r) || bn_cmp(t, SM2_N) == 0) 
		{
    
    
			goto retry;
		}

		// s = ((1 + d)^-1 * (k - r * d)) mod n
		fn_mul(t, r, d);
		fn_sub(k, k, t);
		fn_mul(s, d_inv, k);

		// check s != 0
		if (bn_is_zero(s)) 
		{
    
    
			goto retry;
		}

		bn_to_bytes(r, sig->r);
		bn_to_bytes(s, sig->s);

		memset(d, 0, sizeof(d));
		memset(d_inv, 0, sizeof(d_inv));
		memset(k, 0, sizeof(k));
		memset(t, 0, sizeof(t));
		return 1;
	}

	int do_verify(const SM2_KEY* key, const uint8_t dgst[32], const SM2_SIGNATURE* sig)
	{
    
    
		SM2_JACOBIAN_POINT _P, * P = &_P;
		SM2_JACOBIAN_POINT _R, * R = &_R;
		SM2_BN r;
		SM2_BN s;
		SM2_BN e;
		SM2_BN x;
		SM2_BN t;

		// parse public key
		jacobian_point_from_bytes(P, (const uint8_t*)&key->public_key);

		// parse signature values
		bn_from_bytes(r, sig->r);
		bn_from_bytes(s, sig->s);

		// check r, s in [1, n-1]
		if (bn_is_zero(r) == 1
			|| bn_cmp(r, SM2_N) >= 0
			|| bn_is_zero(s) == 1
			|| bn_cmp(s, SM2_N) >= 0) 
		{
    
    
			return -1;
		}

		// e = H(M)
		bn_from_bytes(e, dgst);

		// t = r + s (mod n), check t != 0
		fn_add(t, r, s);
		if (bn_is_zero(t)) 
		{
    
    
			return -1;
		}

		// Q = s * G + t * P
		jacobian_point_mul_sum(R, t, P, s);
		jacobian_point_get_xy(R, x, NULL);

		// r' = e + x (mod n)
		if (bn_cmp(e, SM2_N) >= 0) 
		{
    
    
			bn_sub(e, e, SM2_N);
		}
		if (bn_cmp(x, SM2_N) >= 0) 
		{
    
    
			bn_sub(x, x, SM2_N);
		}
		fn_add(e, e, x);

		// check if r == r'
		if (bn_cmp(e, r) != 0) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int sign(const SM2_KEY* key, const uint8_t dgst[32], uint8_t* sigbuf, size_t* siglen, bool rawRS)
	{
    
    
		SM2_SIGNATURE sig;

		if (!key || !dgst || !sigbuf || !siglen) 
		{
    
    
			return -1;
		}

		if (sm2_do_sign(key, dgst, &sig) != 1) 
		{
    
    
			return -1;
		}

		if (rawRS)
		{
    
    
			*siglen = sizeof(SM2_SIGNATURE);
			memcpy(sigbuf, &sig, *siglen);
			return 1;
		}

		*siglen = 0;
		if (signature_to_der(&sig, &sigbuf, siglen) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int verify(const SM2_KEY* key, const uint8_t dgst[32], const uint8_t* sigbuf, size_t siglen, bool rawRS)
	{
    
    
		SM2_SIGNATURE sig;

		if (!key || !dgst || !sigbuf || !siglen)
		{
    
    
			return -1;
		}

		if (rawRS)
		{
    
    
			if (siglen != sizeof(SM2_SIGNATURE)) 
			{
    
    
				return -1;
			}
			memcpy(&sig, sigbuf, siglen);
		}
		else
		{
    
    
			if (signature_from_der(&sig, &sigbuf, &siglen) != 1
				|| asn1_length_is_zero(siglen) != 1) 
			{
    
    
				return -1;
			}
		}

		if (do_verify(key, dgst, &sig) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int compute_z(uint8_t z[32], const SM2_POINT* pub, const char* id, size_t idlen)
	{
    
    
		sm3_context ctx;
		uint8_t zin[18 + 32 * 6] = 
		{
    
    
			0x00, 0x80,
			0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,
			0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
			0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,
			0x28,0xE9,0xFA,0x9E,0x9D,0x9F,0x5E,0x34,0x4D,0x5A,0x9E,0x4B,0xCF,0x65,0x09,0xA7,
			0xF3,0x97,0x89,0xF5,0x15,0xAB,0x8F,0x92,0xDD,0xBC,0xBD,0x41,0x4D,0x94,0x0E,0x93,
			0x32,0xC4,0xAE,0x2C,0x1F,0x19,0x81,0x19,0x5F,0x99,0x04,0x46,0x6A,0x39,0xC9,0x94,
			0x8F,0xE3,0x0B,0xBF,0xF2,0x66,0x0B,0xE1,0x71,0x5A,0x45,0x89,0x33,0x4C,0x74,0xC7,
			0xBC,0x37,0x36,0xA2,0xF4,0xF6,0x77,0x9C,0x59,0xBD,0xCE,0xE3,0x6B,0x69,0x21,0x53,
			0xD0,0xA9,0x87,0x7C,0xC6,0x2A,0x47,0x40,0x02,0xDF,0x32,0xE5,0x21,0x39,0xF0,0xA0,
		};

		if (!z || !pub || !id) 
		{
    
    
			return -1;
		}

		memcpy(&zin[18 + 32 * 4], pub->x, 32);
		memcpy(&zin[18 + 32 * 5], pub->y, 32);

		SM3::Starts(&ctx);
		if (strcmp(id, SM2_DEFAULT_ID) == 0) 
		{
    
    
			SM3::Update(&ctx, zin, sizeof(zin));
		}
		else 
		{
    
    
			uint8_t idbits[2];
			idbits[0] = (uint8_t)(idlen >> 5);
			idbits[1] = (uint8_t)(idlen << 3);
			SM3::Update(&ctx, idbits, 2);
			SM3::Update(&ctx, (uint8_t*)id, idlen);
			SM3::Update(&ctx, zin + 18, 32 * 6);
		}
		SM3::Finish(&ctx, z);
		return 1;
	}

	int sign_init(sm3_context* ctx, const SM2_KEY* key, const char* id, size_t idlen)
	{
    
    
		if (!ctx || !key) 
		{
    
    
			return -1;
		}
		SM3::Starts(ctx);

		if (id) 
		{
    
    
			uint8_t z[32];
			if (idlen <= 0 || idlen > SM2_MAX_PLAINTEXT_SIZE) 
			{
    
    
				return -1;
			}
			compute_z(z, &key->public_key, id, idlen);
			SM3::Update(ctx, z, sizeof(z));
		}
		return 1;
	}

	int sign_update(sm3_context* ctx, const uint8_t* data, size_t datalen)
	{
    
    
		if (!ctx) 
		{
    
    
			return -1;
		}
		if (data && datalen > 0) 
		{
    
    
			SM3::Update(ctx, (uint8_t*)data, datalen);
		}
		return 1;
	}

	int sign_finish(sm3_context* ctx, const SM2_KEY* key, uint8_t* sig, size_t* siglen, bool rawRS)
	{
    
    
		uint8_t dgst[32];

		if (!ctx || !key || !sig || !siglen) 
		{
    
    
			return -1;
		}
		SM3::Finish(ctx, dgst);
		if (sign(key, dgst, sig, siglen, rawRS) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int verify_init(sm3_context* ctx, const SM2_KEY* key, const char* id, size_t idlen)
	{
    
    
		if (!ctx || !key) 
		{
    
    
			return -1;
		}
		SM3::Starts(ctx);

		if (id) 
		{
    
    
			uint8_t z[32];
			if (idlen <= 0 || idlen > SM2_MAX_PLAINTEXT_SIZE) 
			{
    
    
				return -1;
			}
			compute_z(z, &key->public_key, id, idlen);
			SM3::Update(ctx, z, sizeof(z));
		}
		return 1;
	}

	int verify_update(sm3_context* ctx, const uint8_t* data, size_t datalen)
	{
    
    
		if (!ctx) 
		{
    
    
			return -1;
		}
		if (data && datalen > 0) 
		{
    
    
			SM3::Update(ctx, (uint8_t*)data, datalen);
		}
		return 1;
	}

	int verify_finish(sm3_context* ctx, const SM2_KEY* key, const uint8_t* sig, size_t siglen, bool rawRS)
	{
    
    
		uint8_t dgst[32];

		if (!ctx || !sig) 
		{
    
    
			return -1;
		}
		SM3::Finish(ctx, dgst);
		if (verify(key, dgst, sig, siglen, rawRS) != 1) 
		{
    
    
			return -1;
		}
		return 1;
	}

	int GetPubKeyOfCerStr(const char* cer, size_t cerLen, char* pubKey)
	{
    
    
		if (!cer || !cerLen || cer[0] != '0') {
    
    
			return 0;
		}

		unsigned char subjectPublicKeyHead[25] = {
    
     0 };
		subjectPublicKeyHead[0] = '\x30';//表示一个Sequence(Subject Public Key Info)
		subjectPublicKeyHead[1] = '\x59';//后续字节数
		subjectPublicKeyHead[2] = '\x30';//表示一个Sequence(Public Key Algorithm)
		subjectPublicKeyHead[3] = '\x13';//后续字节数
		subjectPublicKeyHead[4] = '\x06';//表示一个ObjectID:1.2.840.10045.2.1 Elliptic curve public key cryptography
		subjectPublicKeyHead[5] = '\x07';//后续字节数
		subjectPublicKeyHead[6] = '\x2A';
		subjectPublicKeyHead[7] = '\x86';
		subjectPublicKeyHead[8] = '\x48';
		subjectPublicKeyHead[9] = '\xCE';
		subjectPublicKeyHead[10] = '\x3D';
		subjectPublicKeyHead[11] = '\x02';
		subjectPublicKeyHead[12] = '\x01';
		subjectPublicKeyHead[13] = '\x06';//表示一个ObjectID:1.2.156.10197.1.301 "SM2" elliptic curve cryptography
		subjectPublicKeyHead[14] = '\x08';//后续字节数
		subjectPublicKeyHead[15] = '\x2A';
		subjectPublicKeyHead[16] = '\x81';
		subjectPublicKeyHead[17] = '\x1C';
		subjectPublicKeyHead[18] = '\xCF';
		subjectPublicKeyHead[19] = '\x55';
		subjectPublicKeyHead[20] = '\x01';
		subjectPublicKeyHead[21] = '\x82';
		subjectPublicKeyHead[22] = '\x2D';
		subjectPublicKeyHead[23] = '\x03';//表示一个BitString类型(Public-Key)
		subjectPublicKeyHead[24] = '\x42';//后续字节数
		size_t hdrLen = sizeof(subjectPublicKeyHead);

		unsigned char ch;
		for (size_t i = 0; i < cerLen - hdrLen; i++) {
    
    
			if (memcmp(cer + i, subjectPublicKeyHead, hdrLen) == 0) {
    
    
				for (size_t j = 2; j < 0x42; j++) {
    
    
					ch = (cer[i + hdrLen + j] & 0xf0) >> 4;
					pubKey[(j - 2) * 2] = (ch > 9) ? ch + 'A' - 10 : ch + '0';

					ch = cer[i + hdrLen + j] & 0x0f;
					pubKey[(j - 2) * 2 + 1] = (ch > 9) ? ch + 'A' - 10 : ch + '0';
				}
				return 1;
			}
		}

		return 0;
	}
};

#endif // !__SM2_H__

5.2 单元测试

void TestSM2()
{
    
    
    const char* priKey = "ee9751a685f4f1ca1ef355b15b937475f90757c6ea930f04a3242901a13b94a2";
    const char* pubKey = "04de16070290a70b3457fef7651c5e3be8902b5d589f3d0b80efc5f4e38a78d0e7d09b9d82acc2f31eb6314f831f0766cf644c1d5856794b9106b5f964b7b6fb5d";
    const char* pMsg = "Hello, World,I love 中国.";

    SM2 sm2;
    // 初始化密钥
    if (!sm2.InitKey(priKey, pubKey))
    {
    
    
        std::cout << "初始化SM2密钥失败" << std::endl;
        return;
    }
    std::cout << "测试数据:" << strlen(pMsg) << "," << pMsg << std::endl;

    // 签名
    char hex[512] = {
    
     0 };
    char sign[128] = {
    
     0 };
    size_t len = sizeof(sign);
    if (!sm2.SM2Sign((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, &len) || len == 0)
    {
    
    
        std::cout << "生成签名失败" << std::endl;
        return;
    }
    size_t out_len = sizeof(hex);
    memset(hex, 0, sizeof(hex));
    SM2::Bin2Hex(sign, len, hex, out_len);
    std::cout << "生成签名成功" << std::endl;

    if (!sm2.SM2Verify((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, len))
    {
    
    
        std::cout << "签名校验失败" << std::endl;
        return;
    }
    std::cout << "签名校验成功:" << out_len << "," << hex << std::endl;

    // 加密
    char ciphertext[512] = {
    
     0 };
    len = sizeof(ciphertext);
    if (!sm2.SM2Encrypt((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)ciphertext, &len))
    {
    
    
        std::cout << "加密失败" << std::endl;
        return;
    }
    out_len = sizeof(hex);
    memset(hex, 0, sizeof(hex));
    SM2::Bin2Hex(ciphertext, len, hex, out_len);
    std::cout << "加密成功:" << out_len << "," << hex << std::endl;

    char text[512] = {
    
     0 };
    size_t text_len = sizeof(text);
    if (!sm2.SM2Decrypt((unsigned char*)ciphertext, len, (unsigned char*)text, &text_len))
    {
    
    
        std::cout << "解密失败" << std::endl;
        return;
    }
    std::cout << "解密成功:" << text_len << "," << text << std::endl;
}

5.3 输出结果

测试数据:26,Hello, World,I love 中国.
生成签名成功
签名校验成功:144,3046022100945d19ee176dae52533cf0a8af25030204020131d5ac232a0ed1bdb806b2d76d022100b3cdf47895c5e3c390442f7436b0f902b2eb5c8723a70ff8479fcec0b8adae63
加密成功:270,308184022100c1a0e063e981a544f974a9aacd1dd5e193f31378ec52d79e1d4522cd8f0200a0022100de6137f68384f86735c7af776525ab42b1177574305ddeec3f2547161da3ca6a042044e04f1bd288027c43eb99c6a6b1c2071a440f27f07194e0308dfa7530841c26041a926716f58ad2d5bd0c451df6ce1bdef976c608ed03aaf2f7f21f
解密成功:26,Hello, World,I love 中国.

六、结语

SM2作为我国密码技术的核心成果,已在政务、金融、物联网等领域大规模应用。其设计融合了ECC的高效性与抗量子特性,结合国密标准生态(如SM3/SM4),构建了自主可控的安全体系。未来随着抗量子算法的演进,SM2将在后量子密码时代持续发挥关键作用。