SM2加解密代码及算法解析-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、前言

        关于国密算法SM2加解密的标准可参考国标文件

http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=370AF152CB5CA4A377EB4D1B21DECAE0

下文中涉及到的符号约定也可参考国标文件以及我的上一篇分享国密算法SM2 密钥对的生成_xianmie的博客-CSDN博客_sm2 秘钥生成

        想要更清晰明了地了解SM2算法我的建议是要先了解一下椭圆曲线密码学的数学原理大致搞明白椭圆曲线、有限域素域、二元域、椭圆曲线的倍点运算。在此推荐一篇博文可帮助大家更好地理解ECC算法ECC算法简析椭圆曲线密码应用于国密SM2_gentledongyanchao的博客-CSDN博客_ecc算法是国密算法吗

        上一篇博文中已经介绍了SM2算法密钥对的生成在此基础上本文继续介绍SM2的加解密算法。

二、加解密算法流程

官方文档给出的加密算法流程

解密算法流程

      

         为了更加清晰地说明SM2加解密算法到底做了什么我将官方文档中的算法流程进行了总结大家要是看文档看得不清楚可以先看我这个简化版再回过头看文档应该就会清晰很多。我这里以一个加解密的通信过程为例

//密钥对生成

(1)用户A选定一条椭圆曲线Ep(a,b)并取椭圆曲线上一点作为基点G。  

(2)用户A选择一个私钥key由随机数产生并生成公钥P=[key]G倍点运算公钥也是一个点。 

(3)用户AEp(a,b)和点PG传给用户B

//加密  

(4)用户B接到信息后 将待传输的明文M进行摘要提取使用SM3产生一个随机数k计算 t=KDF([k]P)。  

(5)用户B将   密文C1=[k]G C2=M^t异或运算C3=摘要   传给用户A

//解密   

(6)用户A接到信息后计算   

        t’=KDF([key]C1)=KDF([key][k]G)=KDF([k]P)=t     C2^t’=M^t^t’=M即解出明文M

(7) 将解出的明文进行摘要提取验证和C3是否一致若一致则说明解密成功

三、加密代码解释

大家可以对应上文的算法流程及代码的注释来理解

/**
  * @brief  SM2加密
  * @param  msg    要加密的明文数据
  * @param  msglen 明文数据长度
  * @param  wx     公钥的x坐标
  * @param  wxlen  公钥的x坐标长度不超过32
  * @param  wy     公钥的y坐标
  * @param  wylen  公钥的y坐标长度不超过32
  * @param  outmsg 加密后密文 长度为明文 + 96
  * @retval -1失败 msglen + 96成功
  */
int sm2_encrypt(unsigned char *msg,int msglen, unsigned char *wx,int wxlen, \
	              unsigned char *wy,int wylen, unsigned char *outmsg)
{
 
	struct FPECC *cfig = &Ecc256;//椭圆曲线参数
	big x2, y2, c1, c2, k;
	big a,b,p,n,x,y;
	epoint *g, *w;
	int ret = -1;
	int i;
	unsigned char zl[32], zr[32];
	unsigned char *tmp;
	
	miracl instance;
    miracl *mip = &instance;

	tmp = malloc(msglen+64);//分配大小为明文长度+64的内存
	if(tmp == NULL)
		return -1;
	
	mip = mirsys(mip, 20, 0);   //初始化大数系统
	mip->IOBASE = 16;
	
	char mem[MR_BIG_RESERVE(11)];//定义数组以存放12个大数
    memset(mem, 0, MR_BIG_RESERVE(11));
	
	p= mirvar_mem(mip, mem, 0);//初始化大数变量
	a=mirvar_mem(mip, mem, 1);
	b=mirvar_mem(mip, mem, 2);
	n=mirvar_mem(mip, mem, 3);
	x=mirvar_mem(mip, mem, 4);
	y=mirvar_mem(mip, mem, 5);
	k=mirvar_mem(mip, mem, 6);
	x2=mirvar_mem(mip, mem, 7);
	y2=mirvar_mem(mip, mem, 8);
	c1=mirvar_mem(mip, mem, 9);
	c2=mirvar_mem(mip, mem, 10);
	
	cinstr(mip, p,cfig->p);//将字符串转换为整型
	cinstr(mip, a,cfig->a);
	cinstr(mip, b,cfig->b);
	cinstr(mip, n,cfig->n);
	cinstr(mip, x,cfig->x);
	cinstr(mip, y,cfig->y);
	
	ecurve_init(mip, a,b,p,MR_PROJECTIVE);//初始化椭圆曲线

	char mem1[MR_ECP_RESERVE(2)]; //定义数组以存放g、w两点
	memset(mem1 ,0, MR_ECP_RESERVE(2));

	g = epoint_init_mem(mip, mem1,0);//初始化椭圆上的点
	w = epoint_init_mem(mip, mem1,1);
	 
    epoint_set(mip, x,y,0,g);//g=(x,y)为基点G,此x和y为结构体ECC256中的值
	bytes_to_big(mip, wxlen,(char *)wx,x);//将公钥值传入x和y
	bytes_to_big(mip, wylen,(char *)wy,y);
	epoint_set(mip, x,y,0,w);//点w=(x,y),此x和y为公钥的值
	
    //A1
	irand(mip, SEED_CONST);
sm2_encrypt_again:
	do
	{
		bigrand(mip, n, k);
	} while (k->len == 0);//生成随机大数k
	
	//A2:计算C1
	ecurve_mult(mip, k, g, g);//g=[k]g
	epoint_get(mip, g, c1, c2);//将g的坐标取出赋给c1,c2
	big_to_bytes(mip, 32, c1, (char *)outmsg, TRUE);//将g的x坐标转换为字符串作为加密后结果的[0:31]
	big_to_bytes(mip, 32, c2, (char *)outmsg+32, TRUE);//将g的y坐标转换为字符串作为加密后结果的[32:63]
	
	//A3:计算S=[h]PB;若S为无穷远点则报错并退出
	if(point_at_infinity(w))
		goto exit_sm2_encrypt;
	
	//A4:计算椭圆曲线点[k]PB
	ecurve_mult(mip, k, w, w);//w=[k]w
	epoint_get(mip, w, x2, y2);//x2=w(x),y2=w(y)
	big_to_bytes(mip, 32, x2, (char *)zl, TRUE);//将w的x、y坐标转换为字符串后赋给zl和zr
	big_to_bytes(mip, 32, y2, (char *)zr, TRUE);

	//A5:计算t = KDF,如果t全零,返回A1
	if (kdf(zl, zr, msglen, outmsg+64+32) == 0)
		goto sm2_encrypt_again;

	//A6:计算C2=M异或t,(t即outmsg[64+32:64+32+msglen])
	for(i = 0; i < msglen; i++)
	{
		outmsg[64+32+i] ^= msg[i];//此步后C2=outmsg[64:64+msglen]
	}
	//A7:计算C3
	memcpy(tmp, zl, 32);//tmp=x2||msg||y2
	memcpy(tmp+32, msg, msglen);
	memcpy(tmp+32+msglen, zr, 32);
	SM3Calc(tmp, 64+msglen, &outmsg[64]);//C3=Hash(tmp)
	//C=C1||C3||C2,即outmsg[0:63]=C1;outmsg[64:95]=C3;outmsg[96:96+msg]=C2
	ret = msglen+64+32;
	
exit_sm2_encrypt:

	memset(mem,0,MR_BIG_RESERVE(11));
	memset(mem1,0,MR_ECP_RESERVE(2));
	mirexit(mip);
	free(tmp);
	return ret;
}

四、解密代码解释

/**
* @brief  SM2解密
* @param  msg        要解密的密文数据
* @param  msglen     密文数据长度
* @param  privkey    私钥
* @param  privkeylen 私钥长度
* @param  outmsg 解密后的明文 长度为明文 - 96
* @retval -1失败 msglen - 96成功
*/
int sm2_decrypt(unsigned char *msg,int msglen, unsigned char *privkey, \
	              int privkeylen, unsigned char *outmsg)
{
 
	struct FPECC *cfig = &Ecc256;
	big x2, y2, c, k;
	big a,b,p,n,x,y,key1;
	epoint *g;
	unsigned char c3[32];
	unsigned char zl[32], zr[32];
	int i, ret = -1;
	unsigned char *tmp;
	
	miracl instance;
	miracl *mip = &instance;
	
	if(msglen < 96)//长度<96则加密后的数据有问题退出报错
		return 0;
	msglen -= 96;//减去96得到只含密文消息的数据长度
	tmp = malloc(msglen+64);
	if(tmp == NULL)
		return 0;
	
	mip = mirsys(mip, 20, 0);   
	mip->IOBASE = 16;
 
	char mem[MR_BIG_RESERVE(11)];
  memset(mem, 0, MR_BIG_RESERVE(11));
 
	x2 = mirvar_mem(mip, mem, 0);//初始化大数变量
	y2 = mirvar_mem(mip, mem, 1);
	c = mirvar_mem(mip, mem, 2);
	k = mirvar_mem(mip, mem, 3);
	p = mirvar_mem(mip, mem, 4);
	a = mirvar_mem(mip, mem, 5);
	b = mirvar_mem(mip, mem, 6);
	n = mirvar_mem(mip, mem, 7);
	x = mirvar_mem(mip, mem, 8);
	y = mirvar_mem(mip, mem, 9);
	key1 = mirvar_mem(mip, mem, 10);
	
	bytes_to_big(mip, privkeylen,(char *)privkey,key1);//将私钥转换为大数并赋给key1
	
	cinstr(mip, p,cfig->p);//将字符串转换为大数赋给p、a...y
	cinstr(mip, a,cfig->a);
	cinstr(mip, b,cfig->b);
	cinstr(mip, n,cfig->n);
	cinstr(mip, x,cfig->x);
	cinstr(mip, y,cfig->y);
	
	ecurve_init(mip, a,b,p,MR_PROJECTIVE);//初始化椭圆曲线

	char mem1[MR_ECP_RESERVE(1)]; 
	memset(mem1 ,0, MR_ECP_RESERVE(1));

	g = epoint_init_mem(mip, mem1,0);
	
    //B1:取出C1,验证C1是否满足椭圆曲线方程
	bytes_to_big(mip, 32, (char *)msg, x);//加密函数中的c1,c2赋给x,y
	bytes_to_big(mip, 32, (char *)msg+32, y);   	
    if(!epoint_set(mip, x,y,0,g))//检验(c1,c2)是否在椭圆曲线上,若在g=C1
		goto exit_sm2_decrypt; 		
	
	//B2:若S为无穷远点则报错并退出	
	if(point_at_infinity(g))//计算S如果S=[h]C1在无穷远点则返回
		goto exit_sm2_decrypt;  
	
	//B3:计算[dB]C1=(x2,y2),并转换为字符串
	ecurve_mult(mip, key1, g, g);
	epoint_get(mip, g, x2, y2);	//x2=g(x),y2=g(y)
	big_to_bytes(mip, 32, x2, (char *)zl, TRUE);//转换为字符串zl=x2,zr=y2
	big_to_bytes(mip, 32, y2, (char *)zr, TRUE); 
	
	//B4:计算t=KDF(x2||y2,klen),若t全为0则报错退出
	if (kdf(zl, zr, msglen, outmsg) == 0)
		goto exit_sm2_decrypt; 
	
	//B5计算M到outsmg,M=C2异或t
	for(i = 0; i < msglen; i++)
	{
		outmsg[i] ^= msg[i+96];
	}   
	
	//B6:计算u=Hash(x2||M'||y2),若t!=C3则报错退出
	memcpy(tmp, zl, 32);//tmp=zl||outmsg||zr
	memcpy(tmp+32, outmsg, msglen);
	memcpy(tmp+32+msglen, zr, 32);
	
	SM3Calc(tmp, 64+msglen, c3);//计算u
	if(memcmp(c3, msg+64, 32) != 0)
	{
		goto exit_sm2_decrypt;
	}
	
	ret =  msglen;
exit_sm2_decrypt:
	memset(mem,0,MR_BIG_RESERVE(11));
	memset(mem1,0,MR_ECP_RESERVE(1));
	mirexit(mip);
	free(tmp);
	return ret;
}

 

 

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6