当前位置:   article > 正文

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

signedandenvolopeddata

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

1、构造流程

根据RFC规范,在结合GM/T 0010规范的要求,带签名的数字信封由任意类型的加密内容、加了密的一个/多个接收者的内容加密密钥和一个/多个签名者的签名组成。其生成过程如下:
       a. 发送方随机产生一个对应于特定加密算法的内容加密密钥(即会话密钥)。
       b. 发送方将内容加密密钥用每个接收者的公钥加密。(算法为:sm2-3 公钥加密算法   OID:1.2.156.10197.1.301.3)
       c. 对于每一个接收者,把加了密的内容加密密钥和接收者的其他信息放入RecipientInfo值中。
       d. 对于每一个签名者,他用自己的消息摘要算法计算出摘要值 (如果两个签名者使用同样的算法,那么摘要值只需计算一次) 。(根据gmt0010规范,摘要算法为SGD_SM3)
       e. 对于每一个签名者,消息摘要用自己的私钥加密(即签名)。
       f. 对于每一个签名者,把上一步的签名结果和其他的签名者特定信息放入SignerInfo 值中。
       g. 用内容加密密钥加密内容。
       h. 把所有签名者的消息摘要算法、所有签名者的SignerInfo值、所有接收者的RecipientInfo值和加了密的内容一起放入SignedAndEnvelopedData 值中。   

2、编码实现

国密规范中定义的数字信封结构与RFC规范中一致,因此可直接使用gmssl中定义的PKCS7_SIGN_ENVELOPE结构。

然后通过ASN.1库自定义oid类型为signedAndEnvelopedData(1.2.156.10197.6.1.4.2.4)的类型来实现DER编码。自定义ASN结构方案请查看小编之前的文章。这里就不在赘述了。

/*******************************************************

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

OK 小编已自定义好了针对数字信封数据的结构(SM2SignedAndEnvelopedData);

然后根据构造流程中,调用相关算法计算得到了会话密钥密文(经接收者公钥加密)、内容经私钥签名得到的签名值、被会话密钥加密后的内容密文;并将密文/签名值结构数据均进行了DER编码。

*******************************************************/

构造部分主要代码如下:

  1. if ((p7 = SM2SignedAndEnvelopedData_new()) == NULL)
  2. {
  3. //TODO. 错误处理
  4. goto end;
  5. }
  6. //------------设置类型为sm2_signedAndEnveloped
  7. if (!SM2_SignedAndEnvelopedData_set_type(p7, OID_SM2_SignedAndEnveloped)) {
  8. //TODO. 错误处理
  9. goto end;
  10. }
  11. //------------构造recipientinfo
  12. PKCS7_RECIP_INFO *ri = NULL;
  13. if ((ri = PKCS7_RECIP_INFO_new()) == NULL)
  14. {
  15. //TODO. 错误处理
  16. goto end;
  17. }
  18. //设置版本信息
  19. if (!ASN1_INTEGER_set(ri->version, 0))
  20. {
  21. //TODO. 错误处理
  22. goto end;
  23. }
  24. //填充issuer_and_serial 颁发者信息
  25. if (!X509_NAME_set(&ri->issuer_and_serial->issuer, X509_get_issuer_name(enc_cert)))
  26. {
  27. //TODO. 错误处理
  28. goto end;
  29. }
  30. ASN1_INTEGER_free(ri->issuer_and_serial->serial);
  31. if (!(ri->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get_serialNumber(enc_cert))))
  32. {
  33. //TODO. 错误处理
  34. goto end;
  35. }
  36. //设置接收者证书
  37. ri->cert = enc_cert; //注。ri->cert已指向了enc_cert, enc_cert内存将由p7对象释放
  38. //指定算法和相应的参数 //digestAlgorithms 用接收者公钥加密数据加密密钥的算法
  39. //NID_sm2encrypt "1.2.156.10197.1.301.3" /*sm2-3 公钥加密算法*/
  40. X509_ALGOR_set0(ri->key_enc_algor, OBJ_nid2obj(NID_sm2encrypt), V_ASN1_NULL, NULL);
  41. //3. 把加了密的内容加密密钥(即会话密钥)放入RecipientInfo值中。
  42. /*ASN1_STRING_set0内部是直接进行指针指向,故而ek内存将由ASN1对象去释放。将ek指向NULL,防止重复释放*/
  43. ASN1_STRING_set0(ri->enc_key, ek, ekLen);
  44. ek = NULL;
  45. //------------将接收者加入到PKCS7_ENVELOPE的接收者信息集合
  46. if (!SM2_SignedAndEnvelopedData_add_recipient_info(p7, ri))
  47. {
  48. //TODO. 错误处理
  49. goto end;
  50. }
  51. //------------构造签名者信息signerinfo
  52. PKCS7_SIGNER_INFO *si = NULL;
  53. if ((si = PKCS7_SIGNER_INFO_new()) == NULL)
  54. {
  55. //TODO. 错误处理
  56. goto end;
  57. }
  58. /* We now need to add another PKCS7_SIGNER_INFO entry */
  59. if (!ASN1_INTEGER_set(si->version, 1))
  60. {
  61. //TODO. 错误处理
  62. goto end;
  63. }
  64. if (!X509_NAME_set(&si->issuer_and_serial->issuer, X509_get_issuer_name(sig_cert)))
  65. {
  66. //TODO. 错误处理
  67. goto end;
  68. }
  69. /*
  70. * because ASN1_INTEGER_set is used to set a 'long' we will do things the ugly way.
  71. */
  72. ASN1_INTEGER_free(si->issuer_and_serial->serial);
  73. if (!(si->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get_serialNumber(sig_cert))))
  74. {
  75. //TODO. 错误处理
  76. goto end;
  77. }
  78. /* Set the digest algorithms 对内容进行摘要计算的消息摘要算法(本规范采用sm3 "1.2.156.10197.1.401") */
  79. X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(NID_sm3), V_ASN1_NULL, NULL);
  80. /* Set the sign algorithms sm2-1椭圆曲线数字签名算法标识符("1.2.156.10197.1.301.1" ) */
  81. X509_ALGOR_set0(si->digest_enc_alg, OBJ_nid2obj(NID_sm2sign), V_ASN1_NULL, NULL);
  82. //设置用签名者私钥进行签名的结果 DER编码后的数据
  83. /*注。这里用ASN1_STRING_set(),而不用ASN1_STRING_set0(). ASN1_STRING_set()是进行内存拷贝,而ASN1_STRING_set0()中是直接指针指向*/
  84. ASN1_STRING_set(si->enc_digest, &derSignature, derSignatureLen);
  85. //------------将签名者信息加入到PKCS7_ENVELOPE中
  86. if (!SM2_SignedAndEnvelopedData_add_signer(p7, si))
  87. {
  88. //TODO. 错误处理
  89. goto end;
  90. }
  91. //------------将签名者证书添加到PKCS7结构中
  92. if (!SM2_SignedAndEnvelopedData_add_certificate(p7, sig_cert))
  93. {
  94. //TODO. 错误处理
  95. goto end;
  96. }
  97. //------------填充enc_data
  98. if (0 == SM2_SignedAndEnvelopedData_dataInit(p7, uiSymmAlgorithm, iv, 16))
  99. {
  100. //TODO. 错误处理
  101. goto end;
  102. }
  103. if (0 == SM2_SignedAndEnvelopedData_dataFinal(p7, cont, contLen))
  104. {
  105. //TODO. 错误处理
  106. goto end;
  107. }
  108. //------------Der编码
  109. //pucDerSignedAndEnvelopedData为存放DER编码数据的缓冲区
  110. pTmp = NULL;
  111. pTmp = pucDerSignedAndEnvelopedData;
  112. if ((derP7Len = i2d_SM2SignedAndEnvelopedData(p7, &pTmp)) <= 0)
  113. {
  114. //TODO. 错误处理
  115. goto end;
  116. }
  117. end///

SM2_SignedAndEnvelopedData_set_type该方法是根据SM2SignedAndEnvelopedData结构设计的方法,用于根据OID设置其type。 内部会创建PKCS7_SIGN_ENVELOPE数字信封结构对象,并设置PKCS7_SIGN_ENVELOPE对象的版本号以及内容数据类型。(大家可以根据自己自定义的结构去进行设置

  1. int SM2_SignedAndEnvelopedData_set_type(SM2SignedAndEnvelopedData *p7, const char *oid)
  2. {
  3. ASN1_OBJECT *obj;
  4. obj = OBJ_txt2obj(oid, 1);
  5. if (0 == strcmp(oid, OID_SM2_SignedAndEnveloped))
  6. {
  7. p7->type = obj;
  8. if ((p7->signed_and_enveloped = PKCS7_SIGN_ENVELOPE_new()) == NULL)
  9. goto err;
  10. ASN1_INTEGER_set(p7->signed_and_enveloped->version, 1);
  11. if (!ASN1_INTEGER_set(p7->signed_and_enveloped->version, 1))
  12. goto err;
  13. p7->signed_and_enveloped->enc_data->content_type = OBJ_txt2obj(OID_SM2_Data, 1);
  14. }
  15. else
  16. {
  17. LError("SM2_SignedAndEnvelopedData_set_type, PKCS7_R_UNSUPPORTED_CONTENT_TYPE");
  18. goto err;
  19. }
  20. return (1);
  21. err:
  22. return (0);
  23. }

SM2_SignedAndEnvelopedData_add_recipient_info方法是根据SM2SignedAndEnvelopedData结构设计的方法,用于将接收者(recipientInfo)信息添加到SM2SignedAndEnvelopedData结构中。

  1. int SM2_SignedAndEnvelopedData_add_recipient_info(SM2SignedAndEnvelopedData *p7, PKCS7_RECIP_INFO *ri)
  2. {
  3. char objtmp[80] = { 0x00 };
  4. STACK_OF(PKCS7_RECIP_INFO) *sk;
  5. OBJ_obj2txt(objtmp, sizeof(objtmp), p7->type, 1);
  6. if (0 == strcmp(objtmp, OID_SM2_SignedAndEnveloped))
  7. {
  8. sk = p7->signed_and_enveloped->recipientinfo;
  9. }
  10. else
  11. {
  12. LError("SM2_SignedAndEnvelopedData_add_recipient_info, PKCS7_R_WRONG_CONTENT_TYPE");
  13. return (0);
  14. }
  15. if (!sk_PKCS7_RECIP_INFO_push(sk, ri))
  16. return 0;
  17. return (1);
  18. }

SM2_SignedAndEnvelopedData_add_signer方法是根据SM2SignedAndEnvelopedData结构设计的方法,用于将签名者(signerInfo)信息添加到SM2SignedAndEnvelopedData结构中。

  1. int SM2_SignedAndEnvelopedData_add_signer(SM2SignedAndEnvelopedData *p7, PKCS7_SIGNER_INFO *si)
  2. {
  3. char objtmp[80] = { 0x00 };
  4. int i, j, nid;
  5. X509_ALGOR *alg;
  6. STACK_OF(PKCS7_SIGNER_INFO) *signer_sk;
  7. STACK_OF(X509_ALGOR) *md_sk;
  8. OBJ_obj2txt(objtmp, sizeof(objtmp), p7->type, 1);
  9. if (0 == strcmp(objtmp, OID_SM2_SignedAndEnveloped))
  10. {
  11. signer_sk = p7->signed_and_enveloped->signer_info;
  12. md_sk = p7->signed_and_enveloped->md_algs;
  13. }
  14. else
  15. {
  16. LError("SM2_SignedAndEnvelopedData_add_signer, PKCS7_R_WRONG_CONTENT_TYPE");
  17. return (0);
  18. }
  19. nid = OBJ_obj2nid(si->digest_alg->algorithm);
  20. /* If the digest is not currently listed, add it */
  21. j = 0;
  22. for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++) {
  23. alg = sk_X509_ALGOR_value(md_sk, i);
  24. if (OBJ_obj2nid(alg->algorithm) == nid) {
  25. j = 1;
  26. break;
  27. }
  28. }
  29. if (!j) { /* we need to add another algorithm */
  30. if ((alg = X509_ALGOR_new()) == NULL
  31. || (alg->parameter = ASN1_TYPE_new()) == NULL) {
  32. X509_ALGOR_free(alg);
  33. LError("SM2_PKCS7_add_signer, ERR_R_MALLOC_FAILURE");
  34. return (0);
  35. }
  36. alg->algorithm = OBJ_nid2obj(nid);
  37. alg->parameter->type = V_ASN1_NULL;
  38. if (!sk_X509_ALGOR_push(md_sk, alg)) {
  39. X509_ALGOR_free(alg);
  40. return 0;
  41. }
  42. }
  43. if (!sk_PKCS7_SIGNER_INFO_push(signer_sk, si))
  44. return 0;
  45. return (1);
  46. }

SM2_SignedAndEnvelopedData_add_certificate方法是根据SM2SignedAndEnvelopedData结构设计的方法,用于将签名者证书添加到SM2SignedAndEnvelopedData结构中。

  1. int SM2_SignedAndEnvelopedData_add_certificate(SM2SignedAndEnvelopedData *p7, X509 *x509)
  2. {
  3. char objtmp[80] = { 0x00 };
  4. STACK_OF(X509) **sk;
  5. OBJ_obj2txt(objtmp, sizeof(objtmp), p7->type, 1);
  6. if (0 == strcmp(objtmp, OID_SM2_SignedAndEnveloped))
  7. {
  8. sk = &(p7->signed_and_enveloped->cert);
  9. }
  10. else
  11. {
  12. LError("SM2_SignedAndEnvelopedData_add_certificate, PKCS7_R_WRONG_CONTENT_TYPE");
  13. return (0);
  14. }
  15. if (*sk == NULL)
  16. *sk = sk_X509_new_null();
  17. if (*sk == NULL) {
  18. LError("SM2_PKCS7_add_certificate, ERR_R_MALLOC_FAILURE");
  19. return 0;
  20. }
  21. X509_up_ref(x509);
  22. if (!sk_X509_push(*sk, x509)) {
  23. X509_free(x509);
  24. return 0;
  25. }
  26. return (1);
  27. }

SM2_SignedAndEnvelopedData_dataInit、SM2_SignedAndEnvelopedData_dataFinal方法是根据SM2SignedAndEnvelopedData结构设计的方法,用于将加密内容数据添加到SM2SignedAndEnvelopedData结构中。

  1. int SM2_SignedAndEnvelopedData_dataInit(SM2SignedAndEnvelopedData *p7, int symmAlg, unsigned char *iv, unsigned int ivlen)
  2. {
  3. char objtmp[80] = { 0x00 };
  4. X509_ALGOR *xalg = NULL;
  5. OBJ_obj2txt(objtmp, sizeof(objtmp), p7->type, 1);
  6. if (0 == strcmp(objtmp, OID_SM2_SignedAndEnveloped))
  7. {
  8. xalg = p7->signed_and_enveloped->enc_data->algorithm;
  9. }
  10. else
  11. {
  12. LError("SM2_SignedAndEnvelopedData_dataInit, PKCS7_R_WRONG_CONTENT_TYPE");
  13. return (0);
  14. }
  15. //指定内容加密算法
  16. int nid_alg = NID_undef;
  17. switch (symmAlg)
  18. {
  19. case SGD_SM4_ECB:
  20. nid_alg = NID_sms4_ecb;
  21. break;
  22. case SGD_SM4_CBC:
  23. nid_alg = NID_sms4_cbc;
  24. break;
  25. case SGD_SM4_CFB:
  26. nid_alg = NID_sms4_cfb128;
  27. break;
  28. case SGD_SM4_OFB:
  29. nid_alg = NID_sms4_ofb128;
  30. break;
  31. default:
  32. LError("Symm Algorithm undef.");
  33. nid_alg = NID_undef;
  34. return 0;
  35. }
  36. xalg->algorithm = OBJ_nid2obj(nid_alg);
  37. if (xalg->parameter == NULL)
  38. xalg->parameter = ASN1_TYPE_new();
  39. if (xalg->parameter == NULL)
  40. return 0;
  41. if (nid_alg == NID_sms4_ecb)
  42. {
  43. ASN1_TYPE_free(xalg->parameter);
  44. xalg->parameter = NULL;
  45. }
  46. else
  47. {
  48. //SM4算法非ECB模式,需要将IV向量值存放到enc_data中
  49. //存放到V_ASN1_OCTET_STRING类型中
  50. ASN1_TYPE_set_octetstring(xalg->parameter, iv, ivlen);
  51. }
  52. return 1;
  53. }
  54. int SM2_SignedAndEnvelopedData_dataFinal(SM2SignedAndEnvelopedData *p7, unsigned char *cont, unsigned int contlen)
  55. {
  56. char objtmp[80] = { 0x00 };
  57. ASN1_OCTET_STRING *os = NULL;
  58. OBJ_obj2txt(objtmp, sizeof(objtmp), p7->type, 1);
  59. if (0 == strcmp(objtmp, OID_SM2_SignedAndEnveloped))
  60. {
  61. os = p7->signed_and_enveloped->enc_data->enc_data;
  62. if (os == NULL) {
  63. os = ASN1_OCTET_STRING_new();
  64. if (os == NULL) {
  65. LError("SM2_SignedAndEnvelopedData_dataFinal, ERR_R_MALLOC_FAILURE");
  66. return 0;
  67. }
  68. p7->signed_and_enveloped->enc_data->enc_data = os;
  69. }
  70. }
  71. else
  72. {
  73. LError("SM2_SignedAndEnvelopedData_dataFinal, PKCS7_R_WRONG_CONTENT_TYPE");
  74. return (0);
  75. }
  76. if (os == NULL)
  77. return 0;
  78. ASN1_OCTET_STRING_set(os, cont, contlen);
  79. return 1;
  80. }

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

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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/998423
推荐阅读
相关标签
  

闽ICP备14008679号