分享

PKCS#7签名结构解析(一)

 吴雨虹2kzpi83a 2020-06-06

PKCS#7定义了加密消息的语法标准,也就是加密数据、数字信封、数字签名这些密码运算结果的数据格式标准。基于这一标准,使得不同密码体系之间交换数据成为可能。PKCS#7作为RSA安全体系的一部分,被广泛支持和使用,如CryptoAPIOpenSSLPDF加密签名等。但在某些情况下,如Java自带的加密库并不支持PKCS#7,或者使用PKCS#7不支持的国密算法时,就可能需要我们自己实现PKCS#7标准。今天先讲解一下使用最多的PKCS#7的数字签名标准。说明的是,提供的VC代码示例只能是示例,因为完整的代码是比较复杂的,不可能全部提供。主要是给大家起到参考和提示的作用。学习此部分内容,需要一些ASN.1基础。

PKCS#7采用ASN.1语义描述,因此数字签名也需按照其通用语法标准封装成ContentInfo类型,其定义如下。

ContentInfo::= SEQUENCE {

  contentType ContentType,

  content [0] EXPLICIT ANY DEFINED BYcontentType OPTIONAL }

ContentType::= OBJECT IDENTIFIER

示例代码如下:

         CInnerObject<CDerValue,IDerValue> sig;                // CDerValue是自DER编码处理类。这里

         hr=sig.CreateInstance();                                               //创建一个实例,用于生成符合ASN.1

         _handle_result2();                                                           //标准的数据。下同

         hr=sig->put_Tag(TAG_SEQUENCE);                                     // ContentInfo的类型是SEQUENCE

         _handle_result2();

         CInnerObject<CDerValue,IDerValue> ctype;                   

         hr=ctype.CreateInstance();

         _handle_result2();

         BSTR oid =CharToWchar(szOID_PKCS_7_SIGNED); //标识: "1.2.840.113549.1.7.2"

         hr=ctype->put_ObjectIdentifier(oid);                // contentTypeOBJECT IDENTIFIER类型,                                                                                              //PKCS#7数字签名的标识赋值给它。

         _handle_result2();

         hr=sig->AddItem(ctype,&datasize);

         _handle_result2();

         CInnerObject<CDerValue,IDerValue> cdata;     //PKCS7  signedData数据

         hr=cdata.CreateInstance();

         _handle_result2();

         hr=cdata->put_Tag(TAG_OPT);                                    // OPTIONAL类型, content标识为0                                                                                                //所以TAG值设置为TAG_OPT

         _handle_result2();

         hr=sig->AddItem(cdata,&datasize);

         _handle_result2();

数字签名类型为SignedData,定义如下。

SignedData ::= SEQUENCE {

    version Version,

    digestAlgorithms DigestAlgorithmIdentifiers,

    contentInfo ContentInfo,

    certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,

    crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,

    signerInfos SignerInfos

}

首先生成SEQUENCE类型的SignedData,并填充到上面ContentInfo里的content里。

         CInnerObject<CDerValue,IDerValue> sdata;

         hr=sdata.CreateInstance();

         _handle_result2();

         hr=sdata->put_Tag(TAG_SEQUENCE);

         _handle_result2();

         hr=cdata->AddItem(sdata,&datasize);

         _handle_result2();

接下来就按照标准拼装数据,形成一个标准的PKCS#7签名。

1. version

整数类型,指PKCS#7语法的版本,目前取值为1

         CInnerObject<CDerValue,IDerValue> version;                          //版本号

         hr=version.CreateInstance();

         _handle_result2();

         eseals::CBigIntegerPtr ver;                                          //这里用了一个自定义的大整数类处理

         hr=ver.Create(1);                                                                               //设置为1

         hr=version->SetInteger(ver);

         _handle_result2();

         hr=sdata->AddItem(version,&datasize);

         _handle_result2();

2. digestAlgorithms

digestAlgorithms是消息摘要算法标识符的集合, 用来标识每个签名者使用的消息摘要算法ASN.1定义如下。

DigestAlgorithmIdentifiers:= Set of DigestAlgorithmIdentifier

Set of DigestAlgorithmIdentifier

DigestAlgorithmIdentifier ::= AlgorithmIdentifier

AlgorithmIdentifier  ::=  SEQUENCE  {

    algorithm               OBJECT IDENTIFIER,

    parameters              ANY DEFINED BY algorithm OPTIONAL

}

示例代码,使用SM3算法:

         CInnerObject<CDerValue,IDerValue> algorithms;                    //摘要算法集合

         hr=algorithms.CreateInstance();

         _handle_result2();

         hr=algorithms->put_Tag(TAG_SET);                                              //集合类型,这里只有一个元素,                                                                                                               //即单用户签名

         _handle_result2();

         CInnerObject<CDerValue,IDerValue> algsdata;

         hr=algsdata.CreateInstance();

         _handle_result2();

         hr=algsdata->put_Tag(TAG_SEQUENCE);                 // AlgorithmIdentifierSEQUENCE类型      _handle_result2();

         hr=algorithms->AddItem(algsdata,&datasize);

         _handle_result2();

         CInnerObject<CDerValue,IDerValue> algcdata;

         hr=algcdata.CreateInstance();

         _handle_result2();

         //这里用SM3算法标识"1.2.156.10197.1.401",同时没有其他参数。

         hr=algcdata->put_ObjectIdentifier(CharToWchar(szOID_SM3_Hash_Algorithm));

         _handle_result2();

         hr=algsdata->AddItem(algcdata,&datasize);

         _handle_result2();

         hr=sdata->AddItem(algorithms,&datasize);

         _handle_result2();

3. contentInfo

contentInfo是被签名的原文内容。这里又是通用的ContentInfo类型,就不再贴类型定义了。如果contentInfo里的content不存在时,表明签名与正文分离,这也是实际应用中常用的方式。示例如下:

         CInnerObject<CDerValue,IDerValue> content;       //原文     

         hr=content.CreateInstance();

         _handle_result2();

         hr=content->put_Tag(TAG_SEQUENCE);

         _handle_result2();

         CInnerObject<CDerValue,IDerValue> ctncdata;

         hr=ctncdata.CreateInstance();

         _handle_result2();

         //原文类型为数据内容(PKCS7 DATA),同时签名与正文分离,只储存原文类型

         hr=ctncdata->put_ObjectIdentifier(CharToWchar(szOID_PKCS_7_DATA));  

         _handle_result2();

         hr=content->AddItem(ctncdata,&datasize);

         _handle_result2();

         hr=sdata->AddItem(content,&datasize);

         _handle_result2();

4. certificates

certificates是签名使用的证书集合,它是可选的,定义如下。

ExtendedCertificatesAndCertificates:=Set of ExtendedCertificateOrCertificate

Set of ExtendedCertificateOrCertificate

ExtendedCertificateOrCertificate ::= CHOICE {

    certificate Certificate, -- X.509

    extendedCertificate [0] IMPLICIT ExtendedCertificate

}

从定义可以看出,certificates的每一个元素是X.509证书或PKCS6扩展证书。示例里使用的是X509证书,这里通过调用CryptoAPICertCreateCertificateContext方法可以直接生成证书的ASN1编码内容,并赋值给certificate元素,而不需要再根据Certificate类型的定义一个个元素拼装了。示例代码如下。

         CInnerObject<CDerValue,IDerValue> certs;                     //签名所使用的证书

         hr=certs.CreateInstance();

         _handle_result2();

         hr=certs->put_Tag(TAG_OPT);                                              //

         _handle_result2();

         hr=sdata->AddItem(certs,&datasize);

         _handle_result2();

         CMemoryLocator<BYTE> ml;                    // CMemoryLocator是用来处理数据的模板类

         hr = ml.Base64Decode(CertStr);              // CertStr是签名证书的BASE64

         CCertContext pCertContext = ::CertCreateCertificateContext(X509_ASN_ENCODING, ml, ml.GetSize());                                                         //得到ASN1编码的证书内容

         ::memcpy(ml.GetBuffer(),pCertContext->pbCertEncoded,pCertContext->cbCertEncoded);

         CComPtr<IStream> pStm;

         hr=ml.get_IStream(&pStm);                      //得到证书ASN1编码的字节流

         _handle_result2();                                       

         CInnerObject<CDerValue,IDerValue> certsdata;

         hr=certsdata.CreateInstance();

         _handle_result2();

         hr=certsdata->Load(pStm);                                 //加载字节流,得到DER编码数据

         _handle_result2();

         hr=certs->AddItem(certsdata,&datasize);

         _handle_result2();

5. crls

crls是证书吊销列表(CRL)的集合,它也是可选的,其定义如下。所谓证书吊销列表就是由CA发布的、在它所发放证书中已经被吊销的证书的列表名单。通过CRL对比验证,我们可以确定证书是否被吊销。

CertificateRevocationLists:= Set of CertificateRevocationList

Set of CertificateRevocationList

CertificateRevocationList ::= CertificateList

CertificateList ::= SEQUENCE {

    crlToSign           CRLToSign,

    algorithmIdentifier AlgorithmIdentifier,

    signatureValue      BIT STRING

}

在实际使用中,加入CRL验证是一件比较复杂的事情,故此处略去。 

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多