Bootstrap

物联网(国密SM2,SM4和Mqtt)

项目背景

项目涉及的知识点

第1步tcp客户端与服务器的认证过程,即签名、认证、获取秘钥的过程,需要用到国密SM2的签名、加解密。根据微瓴提供的接口api,认证过程分为注册、挑战应答、登录,详细内容可自行查阅微瓴接口api文档https://api.weiling.qq.com/doc/IOT.html.

第2步mqtt客户端与物联网平台通讯过程,需要用到国密SM4的加解密,mqtt协议通讯等相关的知识点



前言

设备需要对接微瓴物联网平台,在里面踩了太多坑了,本着开源的精神,抽空写写总结,感谢网上开源的各路大神的资料,自己总结一篇。


一、相关资料

关于国密的介绍,网上的资料也比较齐全,这里不再详细描述,整理几个博客的资料可以参考下,当然这个流程如果结合代码来看的话就更加通俗易懂。

SM2是国密算法的一部分,于2010年由国密局公布,属于非对称加密算法,本身是基于ECC椭圆曲线算法来实现的。

ECC椭圆曲线加密算法原理 https://blog.csdn.net/jingcheng345413/article/details/54969289.这一篇比较深奥,主要介绍ECC椭圆曲线,椭圆曲线算法生成公钥和私钥,了解下即可。

详解国密SM2的加密和解密 https://blog.csdn.net/mogoweb/article/details/105108689.

图解SM2算法流程(合)https://blog.csdn.net/samsho2/article/details/80772228.这篇主要介绍算法流程,可以深入了解,对理解后面的代码流程有帮助。

国密SM2数字签名算法 https://blog.csdn.net/jingcheng345413/article/details/54969289.
详解国密SM2的数字签名 https://blog.csdn.net/mogoweb/article/details/105304275.


SM2非对称加密的结果由C1,C2,C3三部分组成。其中C1是生成随机数的计算出的椭圆曲线点,C2是密文数据,C3是SM3的摘要值。最开始的国密标准的结果是按C1C2C3顺序的,新标准的是按C1C3C2顺序存放的。但是通常使用过程对结果会进行ASN.1编解码。

关于ASN.1
ASN.1 常用类型 编码详解 入门 https://blog.csdn.net/q1009020096/article/details/86619059.感觉比较类似tlv结构。我在微瓴api文档截了一段密文

30820129022100C15EF8033C36AA13DE38DE14402D737FD56E194F99DBA890E76235B1D812F6FE0220684956A8B3C1E01C556372A93540883DAF462354BC0E34E212B8ECEC96FA2C65042004E6BD33B9D03F53895AB33D72D8A75AC61A81FF10ED86CC5FD4B0F5D0FD68D50481BF6B3F661766323A784159653A74AAF0DAA3DF444773C95B41340CDFD70A8AD4271EB71BA42FE0B7E8469E8E820DDE237507818E521616E76DE548FD205D17C1C6461E7EF2BBBA917F4D8D265A55A662779C8B5EC171B4D34ECF1C323A771ADEA1B28E2108FDF5293F8720A17DF80A33889EF109CEE0A696780F674AD30B5EBD9BB4CC2ED319D4705C30BEE65E764C5376D0C62CEC7655BCD0F58ED98597A93D9B3536FAA2D569366072E208D914414BAC7CB16A3E0D2D84063A1CA09D66FD15

/**** 密文ASN1 Der编码
	3081df

	0220 tag+len 向量x
	641310dca5a294b6c2eb70e1ea706f27e6471c747faffd1315fbb37fbd9e32ff

	0221 tag+len 向量y
	00bd8810fdc6af91de713dc570efb932fe286c040a8d1845b6e6fced37f157a6f3

	0420 tag+len hash值
	84cbf2ed69b8683c2dfe2c97c1ac9c5328808b1acf706ffb20a864c27ec014c5

	0476 tag+len 密文
	a611fc3545da4fcc41706a640b7ab21ee9cb148e2316159b040024ccb96ce195d85acee94c408fc1a3383feb5e5214efb72858ae1b1221b429956ab24adca2f370b8828e057a1f5c82fb789a468c5c5bd80825a09af1fe1696b76d9183cd675755148ec387d6b8afe8ebf054740f4cdf79a0cc975df2
	****/

可以自己用ASN格式解一下加深印象[捂脸]~~~~~~~~


SM4算法原理 https://blog.csdn.net/cg129054036/article/details/83016958.这篇可以看一看,了解流程,对理解代码有帮助。

SM4国密算法实现分析 https://blog.csdn.net/archimekai/article/details/53095993.这篇我这里有提供源码。


IoT–MQTT协议详解 https://blog.csdn.net/anxianfeng55555/article/details/80908795.这篇是分析mqtt协议,了解一下。
MQTT网上的资料也比较多,自行搜索不再详细描述,基于TCP的一个应用层协议。


TCP和MQTT客户端的实现,我这里选用轻量级的开源mongoose,只要一个.h和.c文件即可实现。

二、基于openssl1.1.1pre5 linux版本国密分析

1.SM2加密源码剖析

相关函数的注释,这篇参考!!!!
EC_POINT_new
EC_GROUP_set_curve_GF
EC_POINT_mul
等等函数有介绍

第5章 椭圆曲线
OpenSSL密码库算法笔记目录 https://blog.csdn.net/samsho2/article/details/85838027.
这位大神的其他文章也可以看看!!!

关于EVP openssl之EVP https://www.cnblogs.com/lytwajue/p/6729316.html.

openssl evp 哈希算法(md5,sha1,sha256) https://www.cnblogs.com/cocoajin/p/6119832.html.

我改动的代码如下,原本这个代码只输出C1C2C3,后来我把转asn1编码移植上去加了少量注释(示例):

int SM2_Encrypt(const unsigned char *message,
                const int message_len,
		const char *pub_key,
		uint8_t *ciphertext_buf, unsigned int *ciphertext_len)
{
   
	int error_code;
	unsigned char c1_x[32], c1_y[32], x2[32], y2[32]; //pub_key_x[32], pub_key_y[32],
	unsigned char c1_point[65], x2_y2[64];
	unsigned char *t = NULL;
	BN_CTX *ctx = NULL;
	BIGNUM *bn_k = NULL, *bn_c1_x = NULL, *bn_c1_y = NULL;
	BIGNUM *bn_pub_key_x = NULL, *bn_pub_key_y = NULL;
	BIGNUM *bn_x2 = NULL, *bn_y2 = NULL;
	const BIGNUM *bn_order, *bn_cofactor;
	EC_GROUP *group = NULL;
	const EC_POINT *generator;
	EC_POINT *pub_key_pt = NULL, *c1_pt = NULL, *s_pt = NULL, *ec_pt = NULL;
	const EVP_MD *md;
	EVP_MD_CTX *md_ctx = NULL;
	int i, flag;
    unsigned char* c3;
    size_t C3_size;

	char pub_x[65] = {
   0};
	char pub_y[65] = {
   0};
	memcpy(pub_x, pub_key, 64);
	memcpy(pub_y, pub_key+64, 64);

	error_code = ALLOCATION_MEMORY_FAIL;
	if ( !(t = (unsigned char *)malloc(message_len)) )
	{
   
		goto clean_up;
	}
	if ( !(ctx = BN_CTX_new()) )
	{
   
	        goto clean_up;
	}
	BN_CTX_start(ctx);
	bn_k = BN_CTX_get(ctx);
	bn_c1_x = BN_CTX_get(ctx);
	bn_c1_y = BN_CTX_get(ctx);
	bn_pub_key_x = BN_CTX_get(ctx);
	bn_pub_key_y = BN_CTX_get(ctx);
	bn_x2 = BN_CTX_get(ctx);	
	bn_y2 = BN_CTX_get(ctx);
	if ( !(bn_y2) )
	{
   
		goto clean_up;
	}
	//加载椭圆曲线
	if ( !(group = EC_GROUP_new_by_curve_name(NID_sm2)) )
	{
   
		goto clean_up;
	}
	
	if ( !(pub_key_pt = EC_POINT_new(group)) )
	{
   
		goto clean_up;
	}
	if ( !(c1_pt = EC_POINT_new(group)) )
	{
   
		goto clean_up;
	}
	if ( !(s_pt = EC_POINT_new(group)) )
	{
   
		goto clean_up;
	}
	if ( !(ec_pt = EC_POINT_new(group)) )
	{
   
		goto clean_up;
	}
	
	if ( !(md_ctx = EVP_MD_CTX_new()) )
	{
   
		goto clean_up;
	}	

	error_code = COMPUTE_SM2_CIPHERTEXT_FAIL;
	
	//字符串公钥x向量转化为x大数
	if ( !(BN_hex2bn(&bn_pub_key_x, pub_x)) )
	{
   
		goto clean_up;
	}

	//字符串公钥y向量转化为y大数
	if ( !(BN_hex2bn(&bn_pub_key_y, pub_y)) )
	{
   
		goto clean_up;
	}
	/*
	获取基点      const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group)
	获取阶        int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order)
	获取相伴因子   int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor)
	*/
	if ( !(bn_order = EC_GROUP_get0_order(group)) )
	{
   
		goto clean_up;
	}
	if ( !(bn_cofactor = EC_GROUP_get0_cofactor(group)) )
	{
   
		goto clean_up;
	}
	if ( !(generator = EC_GROUP_get0_generator(group)) )
	{
   
		goto clean_up;
	}
	//通过坐标bn_pub_key_x和bn_pub_key_y设置仿射点pub_key_pt 
	if ( !(EC_POINT_set_affine_coordinates_GFp(group,
	                                           pub_key_pt,
						   bn_pub_key_x,
						   bn_pub_key_y,
						   ctx)) )
	{
   
		goto clean_up;
	}

	/* Compute EC point s = [h]Pubkey, h is the cofactor.
	   If s is at infinity, the function returns and reports an error. */
	//EC_POINT_mul  两个点的多倍点运算 
	if ( !(EC_POINT_mul(group, s_pt, NULL, pub_key_pt, bn_cofactor, ctx)) )
	{
   
		goto clean_up;
	}
	if ( EC_POINT_is_at_infinity(group, s_pt) )
	{
   
		error_code = EC_POINT_IS_AT_INFINITY;
		goto clean_up;
	}
	
	//采用说明sm3计算的哈希值
	md = EVP_sm3(); 	
	///
	C3_size = EVP_MD_size(md);
    
	if ( !(c3 = (unsigned char *)OPENSSL_zalloc(C3_size)) )
	{
   
		goto clean_up;
	}

	///

	do
	{
      //获取随机数K
		if ( !(BN_rand_range(bn_k, bn_order)) )
		{
   
			goto clean_up;
		}
		if ( BN_is_zero(bn_k) )
		{
   
			continue;
		}
		if ( !(EC_POINT_mul(group, c1_pt, bn_k, NULL, NULL, ctx)) )
		{
   
			goto clean_up;
		}
		if ( !(EC_POINT_mul(group, ec_pt, NULL, pub_key_pt, bn_k, ctx)) )
		{
   
			goto clean_up;
		}
		//读取仿射点ec_pt 坐标bn_x2和bn_y2
		if ( !(EC_POINT_get_affine_coordinates_GFp(group,
		                                           ec_pt,
							   bn_x2,
							   bn_y2,
							   ctx)) )
		{
   
			goto clean_up;
		}
		
		if ( BN_bn2binpad(bn_x2,
		                  x2,
				  sizeof(x2)) != sizeof(x2) )
		{
   
			goto clean_up;
		}
		if ( BN_bn2binpad(bn_y2,
		                  y2,
				  sizeof(y2)) != sizeof(y2) )
		{
   
			goto clean_up;
		}
		memcpy(x2_y2, x2, sizeof(x2));
		memcpy((x2_y2 + sizeof(x2)), y2, sizeof(y2));
		
		//KDF计算
		if ( !(ECDH_KDF_X9_62(t,
		                      message_len,
				      x2_y2,
				      sizeof(x2_y2),
				      NULL,
				      0,
				      md)) )
		{
   
			error_code = COMPUTE_SM2_KDF_FAIL;
			goto clean_up;
		}

		/* If each component of t is zero, the random number k 
		   should be re-generated. */
		flag = 1;
		for (i = 0; i < message_len; i++)
		{
   
			if ( t[i] != 0 )
			{
   
				flag = 0;
				break;
			}
		}		
	} 
;