SM2算法的加密签名消息语法规范(三)如何构造signedData

前面的文章中已经介绍了国密规范中的签名数据signedDatal类型。接下来讲一下怎么构造出这种类型的数据~~\( ̄︶ ̄)/ 

1、构造流程

GM/T0010规范中签名数据类型结构定义与RFC规范中一致。根据RFC规范,构造签名数据的过程涉及到以下步骤:
       a. 对于每个签名者,他用消息摘要算法计算出摘要值;(对于GM/T 0010规范,使用摘要算法为SGD_SM3)
       b. 对于每一个签名者,消息摘要和相关的信息用自己的私钥加密(即签名); (对于GM/T 0010规范,使用SM2签名算法)
       c. 对于每一个签名者,把加密的消息摘要和其它的签名者特定信息放入signer_info值中。每个签名者的证书、crl等也在这一步别收集起来;
       d. 把所有签名者的摘要算法、他们的signer_info值和内容一起放进sign值中 

2、编码实现

之前文章已提过由于OID的原因不能直接使用gmssl库中已定义的pkcs7结构,同样其定义PKCS7_SIGNED结构也不能使用。因为PKCS7_SIGNED结构中的被签名的数据内容类型其定义为struct pkcs7_st类型。如下图所示

被签名的数据内容一般我们会填入Data类型数据,即对应RFC规范中OID:1.2.840.113549.1.7.1,在GM/T 0010规范中OID为:1.2.156.10197.6.1.4.2.1

也就是说直接使用PKCS7_SIGNED的话,contents类型的OID就不能设置成国密规范的OID值。因此也需要通过ASN.1库自定义类型来实现。自定义ASN结构方案请查看小编之前的文章。这里就不在赘述了。

//

(=゚ω゚)ノ ---===≡≡≡一顿操作猛如虎......

OK 小编已自定义好了所需要的签名数据结构(SM2_SIGNED)以及针对签名数据的PKCS7结构(SM2SignedData);

然后根据构造流程中,调用相关算法计算得到了摘要值、签名值DER编码后的数据。(具体算法各家公司都自己的算法,或者使用gmssl库的sm2\sm3算法也可)

//

 构造部分主要代码如下:

/*oid refer to GM/T 0006*/
#define OID_SM2_1 "1.2.156.10197.1.301.1"           /*sm2-1 数字签名算法 */
#define OID_SM2_3 "1.2.156.10197.1.301.3"           /*sm2-3 公钥加密算法*/
#define OID_SM3 "1.2.156.10197.1.401"               /*SM3密码杂凑算法*/
#define OID_SM4 "1.2.156.10197.1.104"               /*SM4分组密码算法*/

/*oid refer to GM/T 0010*/
#define OID_SM2_Data               "1.2.156.10197.6.1.4.2.1"    //SM2算法消息语法规范- 数据类型
#define OID_SM2_Signed             "1.2.156.10197.6.1.4.2.2"    //SM2算法消息语法规范- 签名数据类型
#define OID_SM2_Enveloped          "1.2.156.10197.6.1.4.2.3"    //SM2算法消息语法规范- 数字信封数据类型  
#define OID_SM2_SignedAndEnveloped "1.2.156.10197.6.1.4.2.4"    //SM2算法消息语法规范- 签名及数字信封数据类型
#define OID_SM2_Encrypted          "1.2.156.10197.6.1.4.2.5"    //SM2算法消息语法规范- 加密数据类型
#define OID_SM2_KeyAgreementInfo   "1.2.156.10197.6.1.4.2.6"    //SM2算法消息语法规范- 密钥协商数据类型
/签名数据封装
	if ((p7 = SM2SignedData_new()) == NULL)
	{
        //TODO. 错误处理
		goto end;
	}

	//------------设置类型为sm2_signed
	if (!SM2_SignedData_set_type(p7, OID_SM2_Signed)) {
		//TODO. 错误处理
		goto end;
	}
	//------------填入contents 
	//根据规范理解应该为国密定义的oid:"1.2.156.10197.6.1.4.2.1".
	SM2_SignedData_content_new(p7, OID_SM2_Data); 
	ASN1_OCTET_STRING *os = p7->sign->contents->data;
	ASN1_STRING_set(os, pucData, uiDataLen);  //填入签名原文(即需要签名的数据)

	//------------构造签名者信息signerinfo
	PKCS7_SIGNER_INFO *si = NULL;
	if ((si = PKCS7_SIGNER_INFO_new()) == NULL)
	{
		//TODO. 错误处理
		goto end;
	}

	/* We now need to add another PKCS7_SIGNER_INFO entry */
	if (!ASN1_INTEGER_set(si->version, 1))
	{
		//TODO. 错误处理
		goto end;
	}
	if (!X509_NAME_set(&si->issuer_and_serial->issuer, X509_get_issuer_name(sig_cert)))
	{
		//TODO. 错误处理
		goto end;
	}

	/*
	* because ASN1_INTEGER_set is used to set a 'long' we will do things the ugly way.
	*/
	ASN1_INTEGER_free(si->issuer_and_serial->serial);
	if (!(si->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get_serialNumber(sig_cert))))
	{
		//TODO. 错误处理
		goto end;
	}

	/* Set the digest algorithms  对内容进行摘要计算的消息摘要算法(本规范采用sm3 "1.2.156.10197.1.401") */
	X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(NID_sm3), V_ASN1_NULL, NULL);

	/* Set the sign algorithms  sm2-1椭圆曲线数字签名算法标识符("1.2.156.10197.1.301.1" ) */
	X509_ALGOR_set0(si->digest_enc_alg, OBJ_nid2obj(NID_sm2sign), V_ASN1_NULL, NULL);

	//设置用签名者私钥进行签名的结果   DER编码后的数据
	/*注。这里用ASN1_STRING_set(),而不用ASN1_STRING_set0(). ASN1_STRING_set()是进行内存拷贝,而ASN1_STRING_set0()中是直接指针指向*/
	ASN1_STRING_set(si->enc_digest, &derSignature, derSignatureLen);

	//------------将签名者信息加入到PKCS7_ENVELOPE中
	if (!SM2_SignedData_add_signer(p7, si))
	{
		//TODO. 错误处理
		goto end;
	}
	//------------将签名者证书添加到PKCS7结构中
	if (!SM2_SignedData_add_certificate(p7, sig_cert))
	{
		//TODO. 错误处理
		goto end;
	}

	//------------Der编码
    //pucDerSignedData为缓冲区,用来存放DER编码后的数据;
    pTmp = NULL;
	pTmp = pucDerSignedData;
	if ((derP7Len = i2d_SM2SignedData(p7, &pTmp)) <= 0)
	{
		//TODO. 错误处理
		goto end;
	}
	///end

SM2_SignedData_set_type该方法是根据SM2SignedData结构设计的方法,用于根据OID设置其type。内部会创建SM2_SIGNED签名结构对象,并设置SM2_SIGNED对象的版本号。(大家可以根据自己自定义的结构去进行设置

	ASN1_OBJECT *obj;

	obj = OBJ_txt2obj(oid, 1);

	if (0 == strcmp(oid, OID_SM2_Signed))
	{
		p7->type = obj;  //指定OID
        //创建SM2_SIGNED对象
		if ((p7->sign = SM2_SIGNED_new()) == NULL)
			goto err;
        //设置版本号
		if (!ASN1_INTEGER_set(p7->sign->version, 1))
		{
			SM2_SIGNED_free(p7->sign);
			p7->sign = NULL;
			goto err;
		}
	}

SM2_SignedData_content_new方法是根据SM2SignedData结构设计的方法,用于新建一个SM2Data类型对象,并将SM2SignedData结构中contents指向此结构

	SM2Data *ret = NULL;
    //新建SM2Data结构对象
	if ((ret = SM2Data_new()) == NULL)
		goto err;
    //指定SM2Data对象的OID
	if (!SM2_Data_set_type(ret, oid))
		goto err;
    //将SM2Data包含于SMSignedData中
	if (!SM2_SignedData_set_content(p7, ret))
		goto err;

以上仅贴出了一些主要代码,供大家参考的同时也记录下方便自己记忆。

最后DER编码输出的singedData数据用Asn1View打开查看,如下:

    

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sm2算法是一种国家密码学标准算法,它主要用于椭圆曲线公钥密码体制中的加密和数字签名sm2算法基于椭圆曲线离散对数问题,具有安全性和高效性的特点。 sm2算法的C语言实现主要包括以下几个步骤: 1. 选择椭圆曲线参数:sm2算法使用的是国家标准椭圆曲线参数,可以在标准文档中找到。选择合适的参数,初始化椭圆曲线。 2. 生成密钥对:使用随机数生成器生成一个私钥,并通过椭圆曲线上的点运算得到对应的公钥。 3. 加密过程:假设要加密的明文为M,首先将M转换成点P,再生成一个随机数k。通过点运算得到C1=kG,其中G为基点。然后计算点S=kP,将S的x坐标转换成字节数组,作为SM3算法的输入,得到哈希值H。最后计算C2=M⨁H,C3=k^-1·(hash(kP)+C2)。 4. 解密过程:给定密文C=(C1,C2,C3),首先计算C1的k倍点为kC1,并将kC1的x坐标作为SM3算法的输入,得到哈希值H。然后计算C2=M⨁H,并利用C1的k倍点计算kP。最后计算hash(kP)+C2,乘以C3的k^-1得到明文M。 5. 数字签名过程:给定要签名消息M和私钥d,首先计算消息的哈希值H。然后生成一个随机数k,并通过点运算得到点R=kG。将R的x坐标转换成字节数组,作为SM3算法的输入,得到哈希值h。计算s=(h+d·r)·⁻¹·k。最后签名为(R,s)。 以上是sm2算法的简要介绍和C语言实现的整体步骤,实现过程中需注意安全性和正确性。具体的C语言代码实现需要详细研究和阅读sm2算法的相关标准文档,理解椭圆曲线运算和密码哈希算法的具体细节,然后按照算法规范进行编码实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值