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类型。如下图所示

SM2算法的加密签名消息语法规范(三)如何构造signedData_第1张图片

被签名的数据内容一般我们会填入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打开查看,如下:

SM2算法的加密签名消息语法规范(三)如何构造signedData_第2张图片    SM2算法的加密签名消息语法规范(三)如何构造signedData_第3张图片

你可能感兴趣的:(信息安全,网络安全,openssl,密码学)