如何构造PKCS 7签名(二)
- signerInfos
signerInfos是每个签名者信息的集合。它是最复杂也是最重要的部分,其定义如下:
SignerInfos:=Set of SignerInfo
Set of SignerInfo
SignerInfo::= SEQUENCE {
version Version,
issuerAndSerialNumber IssuerAndSerialNumber,
digestAlgorithm DigestAlgorithmIdentifier,
authenticatedAttributes[0] IMPLICIT Attributes OPTIONAL,
digestEncryptionAlgorithmDigestEncryptionAlgorithmIdentifier,
encryptedDigestEncryptedDigest,
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 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.6 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.7 unauthenticatedAttributes
这个元素的定义与authenticatedAttributes一致,区别在于它不参与签名。所以一般用的很少。不过要注意,如果构造这个元素,它在SignerInfo里的tag值应该是TAG_OPT+1。示例就不再添加这个元素了。
最后,生成的签名结构如下: