如何构造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年(清)周述官撰 1
-
少林空悟老师珍藏
- 国术典籍:《增演易筋洗髓内功图说》【2024年8月编校】
-
《增演易筋洗髓内功图说》系养生气功著作,全书共十八卷。清周述官编撰于光绪二十一年(1895年)。清光绪十九年(1893年),僧人静一空悟将少林功法传授于周述官,并将《增益易筋洗髓内功图说》十二卷(按,...
- 小说:自媒体小白的修道之路-洗髓(自媒体小白运营技巧)
-
谁应了谁的劫,谁又变成了谁的执念。当沧海遗忘了桑田,这世间又多了一个不回家的人!异域空间中,知生缓缓起身,目光扫了一下小帝后,又转身看向画板上的那朵白色蒲公英,自言道:“白瑛,这一世我们莫要再辜负了!...
- 这才是少林洗髓经真相:它是静功和导引术与八段锦暗合
-
不少朋友误解易筋经和洗髓经,将其简单归为强力呼吸的吐纳功以及为了提升房中的关窍功。事实上易筋经和洗髓经是两部功法:易筋经主要为炼体,包含以膜论为核心的十二月怕打筋膜法,以及辅助的呼吸、导引功法;洗髓经...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)