百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

如何构造PKCS 7签名 pkcs1签名

lipiwang 2024-10-20 10:11 13 浏览 0 评论


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验证是一件比较复杂的事情,故此处略去。

6. signerInfos

signerInfos是每个签名者信息的集合。它是最复杂也是最重要的部分,其定义如下:

SignerInfos:= Set of SignerInfo

Set of SignerInfo

SignerInfo ::= SEQUENCE {

version Version,

issuerAndSerialNumber IssuerAndSerialNumber,

digestAlgorithm DigestAlgorithmIdentifier,

authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,

digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,

encryptedDigest EncryptedDigest,

unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL

}

示例代码如下,先创建集合signerinfos

CInnerObject<CDerValue,IDerValue> signerinfos; //签名者信息

hr=signerinfos.CreateInstance();

hr=signerinfos->put_Tag(TAG_SET);

_handle_result2();

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

_handle_result2();

CInnerObject<CDerValue,IDerValue> signerdata;

hr=signerdata.CreateInstance();

_handle_result2();

hr=signerdata->put_Tag(TAG_SEQUENCE);

hr=signerinfos->AddItem(signerdata,&datasize); //单用户签名,signerinfos //只有一个元素signerdata

_handle_result2();


接下来为SignerInfo类型元素设置每个属性。

CInnerObject<CDerValue,IDerValue> signerprop;


6.1 version

首先是版本号属性,也是固定为1.

hr=signerprop.CreateInstance(); //5.1 版本

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2();

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

hr=signerprop->SetInteger(ver);

_handle_result2();


6.2 issuerAndSerialNumber

接下来是签名证书的签发者标识及序列号,定义如下。

IssuerAndSerialNumber ::= SEQUENCE {

issuer Name,

serialNumber CertificateSerialNumber

}

示例代码

hr=signerprop.CreateInstance(); //签名证书的签发者标识及序列号

hr=signerprop->put_Tag(TAG_SEQUENCE);

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2();

CInnerObject<CDerValue,IDerValue> propdata;

hr=propdata.CreateInstance();

LONG len=pCertContext->pCertInfo->Issuer.cbData;

if (!ml.Allocat(len))

return E_OUTOFMEMORY;

::memcpy(ml.GetBuffer(),pCertContext->pCertInfo->Issuer.pbData,len);//签发者标识

hr=ml.get_IStream(&pStm);

hr=propdata->Load(pStm);

_handle_result2();

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();

hr=propdata.CreateInstance();

len=pCertContext->pCertInfo->SerialNumber.cbData;

eseals::CBigIntegerPtr sm;

hr=sm.Create(pCertContext->pCertInfo->SerialNumber.pbData,len,false,true);

hr=propdata->SetInteger(sm);

_handle_result2();

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();


6.3 digestAlgorithm

第三个属性是摘要算法标识,这里要与digestAlgorithms里的某一元素一致。

hr=signerprop.CreateInstance(); //摘要算法标识

hr=signerprop->put_Tag(TAG_SEQUENCE);

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2();

hr=propdata.CreateInstance();

//SM3算法

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

_handle_result2();

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();


6.4 authenticatedAttributes

authenticatedAttributes可以被叫做用户认证属性集合元素,定义如下:

Attributes ::= SET OF Attribute

Attribute ::= SEQUENCE

{

type EncodedObjectID,

values AttributeSetValue

}

这个元素是可选的,但如果contentInfo的ContentType不是数据内容(PKCS7 DATA)类型时,这个元素必须有。如果这个元素存在,它至少得有两个子元素:原文类型属性和原文摘要属性。示例代码里设置了三个属性:原文类型属性、时间属性和原文摘要属性。

hr=signerprop.CreateInstance(); //用户认证属性

hr=signerprop->put_Tag(TAG_SET); //这时的signerprop对于要添加的属性数据来 //讲是集合,所以tag要先设置成TAG_SET

_handle_result2();

//------------------------------------------------------------------------

hr=propdata.CreateInstance(); //原文类型

hr=propdata->put_Tag(TAG_SEQUENCE);

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();

CInnerObject<CDerValue,IDerValue> pdata;

hr=pdata.CreateInstance();

//属性标识为"原文类型":"1.2.840.113549.1.9.3"

hr=pdata->put_ObjectIdentifier(CharToWchar(szOID_RSA_contentType));

hr=propdata->AddItem(pdata,&datasize);

CInnerObject<CDerValue,IDerValue> set;

hr=set.CreateInstance();

hr=set->put_Tag(TAG_SET);

hr=propdata->AddItem(set,&datasize);

_handle_result2();

hr=pdata.CreateInstance();

hr=pdata->put_ObjectIdentifier(CharToWchar(szOID_PKCS_7_DATA)); //属性值PKCS7 DATA

_handle_result2();

hr=set->AddItem(pdata,&datasize);

_handle_result2();


//------------------------------------------------------------------------

hr=propdata.CreateInstance(); //签名时间

hr=propdata->put_Tag(TAG_SEQUENCE);

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();

hr=pdata.CreateInstance();

//属性标识为"签名时间":"1.2.840.113549.1.9.5"

hr=pdata->put_ObjectIdentifier(CharToWchar(szOID_RSA_signingTime));

hr=propdata->AddItem(pdata,&datasize);

hr=set.CreateInstance();

hr=set->put_Tag(TAG_SET);

hr=propdata->AddItem(set,&datasize);

_handle_result2();

hr=pdata.CreateInstance();

SYSTEMTIME st;

::GetSystemTime(&st);

WCHAR time[MAX_PATH]=L"";

//ASN1_TIME格式

::_snwprintf(time,MAX_PATH,L"%02d%02d%02d%02d%02d%02d%s",(st.wYear%100),st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,L"Z");

BSTR bstime = ::SysAllocString(time);

hr=pdata->put_Time(bstime); //时间值

_handle_result2();

hr=set->AddItem(pdata,&datasize);

_handle_result2();

//------------------------------------------------------------------------

hr=propdata.CreateInstance(); //原文摘要

hr=propdata->put_Tag(TAG_SEQUENCE);

hr=signerprop->AddItem(propdata,&datasize);

_handle_result2();

hr=pdata.CreateInstance();

//属性标识为"原文摘要":"1.2.840.113549.1.9.4"

hr=pdata->put_ObjectIdentifier(CharToWchar(szOID_RSA_messageDigest));

hr=propdata->AddItem(pdata,&datasize);

hr=set.CreateInstance();

hr=set->put_Tag(TAG_SET);

hr=propdata->AddItem(set,&datasize);

_handle_result2();

hr=pdata.CreateInstance();

if (!ml.Allocat(ulDigestLen))

return E_OUTOFMEMORY;

::memcpy(ml.GetBuffer(),pbDigest,ulDigestLen); //原文摘要值,摘要值pbDigest //的计算过程没有列出

hr=ml.get_IStream(&pStm);

SAFEARRAY* parry;

hr=ml.get_SAFEARRAY(&parry);

hr=pdata->put_Tag(TAG_OCTETSTRING);

hr=pdata->put_Data(parry);

_handle_result2();

hr=set->AddItem(pdata,&datasize);

_handle_result2();


CMemoryLocator<BYTE> pbAuthedAttr; //将用户认证属性值保存到签名数据

hr=pbAuthedAttr.get_IStream(&pStm);

_handle_result2();

hr=signerprop->Save(pStm,FALSE);

_handle_result2();

hr=pbAuthedAttr.put_IStream(pStm);

_handle_result2();

hr=signerprop->put_Tag(TAG_OPT); //这时的signerprop对于要保存到SignerInfo //来讲是OPTIONAL,所以tag要设置成TAG_OPT

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2()


6.5 digestEncryptionAlgorithm

签名算法属性定义如下,与摘要算法一致。

DigestEncryptionAlgorithmIdentifier::= AlgorithmIdentifier

hr=signerprop.CreateInstance(); //签名算法

hr=signerprop->put_Tag(TAG_SEQUENCE);

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2();

hr=propdata.CreateInstance();

hr=propdata->put_ObjectIdentifier(_bstr_t(pCertContext->pCertInfo->SignatureAlgorithm.pszObjId)); //通过证书取得签名算法标识

_handle_result2();

hr=signerprop->AddItem(propdata,&datasize);


6.5 encryptedDigest

终于到了加密的摘要,也就是签名。定义如下

EncryptedDigest ::= OCTET STRING

根据PKCS#7标准,如果没有authenticatedAttributes元素,这里的摘要指原文的摘要;否则就是authenticatedAttributes的摘要,这也是authenticatedAttributes名称的由来。本示例包含代码属于后者。


CCryptoHandle hmHash(this->m_pDriverModule);

//摘要初始化

static const char*USER_ID="1234567812345678";

hr=skf::ParseErrorCode(this->m_pDriverModule->SKF_DigestInit(hDev,SGD_SM3,&eccPubKey,(BYTE*)USER_ID,strlen(USER_ID),&hmHash));

_handle_result2();

BYTE pbDigest2[32]={0};

//生成摘要

hr=skf::ParseErrorCode(this->m_pDriverModule->SKF_Digest(hmHash,pbAuthedAttr.GetBuffer(),pbAuthedAttr.GetSize(),pbDigest2,&ulDigestLen));

_handle_result2();

ECCSIGNATUREBLOB eccSignBlob;

//生成签名值

hr=skf::ParseErrorCode(this->m_pDriverModule->SKF_ECCSignData(hContainer,pbDigest2,ulDigestLen,&eccSignBlob));

_handle_result2();

这里的例子是用国密算法SKF库,所以如果对例子里的函数不了解没关系,只要知道过程即可。注意这里是对pbAuthedAttr计算摘要值。接下来根据国密标准将签名结果进行ASN.1编码

hr=signerprop.CreateInstance(); //签名值ASN.1编码

//根据国密标准进行转换

......

hr=signerdata->AddItem(signerprop,&datasize);

_handle_result2();

代码执行到这里,一开始创建的sig对象已经包含了完整的数字签名。但根据标准,还有一个unauthenticatedAttributes元素。

6.5 unauthenticatedAttributes

这个元素的定义与authenticatedAttributes一致,区别在于它不参与签名。所以一般用的很少。不过要注意,如果构造这个元素,它在SignerInfo里的tag值应该是TAG_OPT+1.

最后,生成的签名结构如下:


相关推荐

微软Office Open XML中的数字签名漏洞

MicrosoftOffice是最广泛使用的办公文档应用程序之一。对于重要文件,如合同和发票,可以对其内容进行签名,以确保其真实性和完整性。自2019年以来,安全研究人员发现了针对PDF和ODF等其...

Javaweb知识 day12 XML(javaweb中xml作用)

一、XML:1.1概念:ExtensibleMarkupLanguage可扩展标记语言*可扩展:标签都是自定义的。<user><student>1.2功能:...

易筋洗髓功——内外同修方可致远(易筋洗髓功口诀)

达摩祖师所传易筋、洗髓两经,一分为二,二实为一,无非以方便法门接引众生,而归于慈悲清净之心地。修炼《易筋经》是为强身健体,修炼《洗髓经》是为修心养性,此二者相辅相成,内外兼修,缺一不可。这是一套传统中...

《增演易筋洗髓内功图说》17卷(1930年(清)周述官撰 4

《增演易筋洗髓内功图说》17卷(1930年(清)周述官撰 5

道家洗髓功修炼要义,洗髓功如何做到丹田聚气?

不管是道家洗髓功,还是洗髓经,其修炼的关键点就在于得气、行气、聚气...那么,作为洗髓功修炼者,具体该怎么做呢?在实际修炼中,就洗髓功的修炼方法来讲,我们可以简单的归纳为修炼三部曲,其具体表现如下:一...

「清风聊练功」师门传我易筋经:聊聊我的学习经历和正身图感受

一个人的眼界认识,是随着是自身的知识积累和水平不断成长的。开篇为什么要说这么一句呢?是从我的学习经历上感受明显的这句话:一处不到一处迷。我们学传统武术,内功功法,也是从小白到明白一步步走的,走的越远,...

内功外练功介绍(练内功 外功)

这里介绍我练习的两套动功心得体会。是老道长的八部金刚功、长寿功和增演易筋洗髓经。八部金刚功外练奇经八脉,练出健康强壮的好身体还是可以的,长寿功也是内练功法。这部功法很好的预防效果。这个大家都认同的。说...

孔德易筋洗髓大全注解(下)(孔德易筋经教学视频)

...

《增演易筋洗髓内功图说》17卷(1930年(清)周述官撰 1

少林空悟老师珍藏

国术典籍:《增演易筋洗髓内功图说》【2024年8月编校】

《增演易筋洗髓内功图说》系养生气功著作,全书共十八卷。清周述官编撰于光绪二十一年(1895年)。清光绪十九年(1893年),僧人静一空悟将少林功法传授于周述官,并将《增益易筋洗髓内功图说》十二卷(按,...

小说:自媒体小白的修道之路-洗髓(自媒体小白运营技巧)

谁应了谁的劫,谁又变成了谁的执念。当沧海遗忘了桑田,这世间又多了一个不回家的人!异域空间中,知生缓缓起身,目光扫了一下小帝后,又转身看向画板上的那朵白色蒲公英,自言道:“白瑛,这一世我们莫要再辜负了!...

这才是少林洗髓经真相:它是静功和导引术与八段锦暗合

不少朋友误解易筋经和洗髓经,将其简单归为强力呼吸的吐纳功以及为了提升房中的关窍功。事实上易筋经和洗髓经是两部功法:易筋经主要为炼体,包含以膜论为核心的十二月怕打筋膜法,以及辅助的呼吸、导引功法;洗髓经...

孔德易筋洗髓大全注解(上)(孔德易筋经洗髓经视频)

...

洗髓经传承与心得(二)(《洗髓经》)

...

取消回复欢迎 发表评论: