前面的文章中已经介绍了国密规范中的签名数据signedDatal类型。接下来讲一下怎么构造出这种类型的数据~~\( ̄︶ ̄)/
GM/T0010规范中签名数据类型结构定义与RFC规范中一致。根据RFC规范,构造签名数据的过程涉及到以下步骤:
a. 对于每个签名者,他用消息摘要算法计算出摘要值;(对于GM/T 0010规范,使用摘要算法为SGD_SM3)
b. 对于每一个签名者,消息摘要和相关的信息用自己的私钥加密(即签名); (对于GM/T 0010规范,使用SM2签名算法)
c. 对于每一个签名者,把加密的消息摘要和其它的签名者特定信息放入signer_info值中。每个签名者的证书、crl等也在这一步别收集起来;
d. 把所有签名者的摘要算法、他们的signer_info值和内容一起放进sign值中
之前文章已提过由于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打开查看,如下: