如何构造PKCS 7签名 pkcs1签名
lipiwang 2024-10-20 10:11 12 浏览 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.
最后,生成的签名结构如下:
相关推荐
- 前端入门——css 网格轨道详细介绍
-
上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...
- Islands Architecture(孤岛架构)在携程新版首页的实践
-
一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...
- HTML中script标签中的那些属性
-
HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...
- CSS 中各种居中你真的玩明白了么
-
页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...
- CSS样式更改——列表、表格和轮廓
-
上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...
- 一文吃透 CSS Flex 布局
-
原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...
- css实现多行文本的展开收起
-
背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...
- css 垂直居中的几种实现方式
-
前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...
- WordPress固定链接设置
-
WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...
- 面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿
-
前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...
- 3种CSS清除浮动的方法
-
今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...
- 2025 年 CSS 终于要支持强大的自定义函数了?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...
- css3属性(transform)的一个css3动画小应用
-
闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...
- CSS基础知识(七)CSS背景
-
一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...
- CSS 水平居中方式二
-
<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 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)