PKCS#7定义了加密消息的语法标准,也就是加密数据、数字信封、数字签名这些密码运算结果的数据格式标准。基于这一标准,使得不同密码体系之间交换数据成为可能。PKCS#7作为RSA安全体系的一部分,被广泛支持和使用,如CryptoAPI、OpenSSL、PDF加密签名等。但在某些情况下,如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); // contentType是OBJECT 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); // AlgorithmIdentifier为SEQUENCE类型 _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证书或PKCS#6扩展证书。示例里使用的是X509证书,这里通过调用CryptoAPI的CertCreateCertificateContext方法可以直接生成证书的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验证是一件比较复杂的事情,故此处略去。 |
|
来自: 吴雨虹2kzpi83a > 《密码学》