如何构造PKCS7签名(三)

所谓有来有往,前面介绍了如何拼装构造PKCS#7签名,今天就讲一下如何验证它。如果已经掌握了构造的方法,验证就相对容易很多。最核心的就是读取signerInfos(签名者信息)里的authenticatedAttributes(用户认证属性)和encryptedDigest(签名),然后进行验证。

首先读取签名用的证书,这里用了遍历,也可以直接取索引值3去访问。

//检查DER数据类型

ASN1_TAG tag=TAG_UNIVERSAL;

hr=signDer->get_Tag(&tag);

if (tag!=TAG_SEQUENCE)

_raise_error(不合理的ASN1类型。);

CComPtr dv1,dv2,dvopt,dvsigner,dvseq,dvauthed,dvsig;

CComPtr dvcert,dvattr,dvattype,dvdigest,dvset;

//读取证书

//biceng::CBytesSafeArray bcert;

hr=signDer->get_Item(1,&dv1);

if (hr!=S_OK)

_raise_error(解析数据失败,读取签名数据失败。);

hr=dv1->get_Item(0,&dv2);

if (hr!=S_OK)

_raise_error(解析数据失败。);

LONG iCount=0;

int sd_index;

hr=dv2->get_Count(&iCount);

for(sd_index=0;sd_index

hr=dv2->get_Item(sd_index,&dvopt);

hr=dvopt->get_Tag(&tag);

if (tag==TAG_OPT){ //检查tag值是否为TAG_OPT

hr=dvopt->get_Item(0,&dvcert);

if (hr!=S_OK)

_raise_error(获取证书数据失败。);

break;

}

}

if(!dvcert)

_raise_error(未找到证书数据。);

hr=cert.get_IStream(&pStm);

hr=dvcert->SaveBigInteger(pStm,FALSE);

hr=cert.put_IStream(pStm);

if (hr!=S_OK)

_raise_error(获取证书数据失败。);

CCertContext pCertContext=::CertCreateCertificateContext(X509_ASN_ENCODING, cert, cert.GetSize());

if (!pCertContext)

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

// 创建哈希对象

CAutoRelease hHash;

CAutoRelease hPubKey;

CERT_PUBLIC_KEY_INFO cpki=pCertContext->pCertInfo->SubjectPublicKeyInfo;

//把签名者的公钥数据导入公钥句柄

if(!co.CPImportKey(cpki.PublicKey.pbData, cpki.PublicKey.cbData, 0, 0, &hPubKey))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

sd_index++;

hr=dv2->get_Item(sd_index,&dvsigner); //读下一个der

if (hr!=S_OK)

_raise_error(解析数据失败。);

hr=dvsigner->get_Tag(&tag);

if (tag==TAG_OPT+1){ //如果这der的tag是TAG_OPT+1,说明是

//证书crl列表元素,那继续读下一个der

sd_index++;

hr=dv2->get_Item(sd_index,&dvsigner);

if (hr!=S_OK)

_raise_error(解析数据失败。);

hr=dvsigner->get_Tag(&tag);

}

开始读取签名者信息。在读取用户认证属性时,属性是没有顺序的,所以只能通过遍历并比较属性类型确定属性内容。另外,根据PKCS#7的标准,如果签名里有用户认证属性,则必须验证原文的摘要和签名的摘要是否一致。

if (tag!=TAG_SET)

_raise_error(获取签名者信息失败,不合理的ASN1类型。);

biceng::CBytesSafeArray bsig,bdigest;

hr=dvsigner->get_Item(0,&dvseq);

if (hr!=S_OK)

_raise_error(解析数据失败。);

int sg_index;

iCount=0;

hr=dvseq->get_Count(&iCount);

for(sg_index=0;sg_index

hr=dvseq->get_Item(sg_index,&dvopt);

hr=dvopt->get_Tag(&tag);

if (tag==TAG_OPT){ //遍历元素,第一个tag值为TAG_OPT的是用户认证属性

dvopt.CopyTo(&dvauthed);

break;

}

}

CMemoryLocator pbAuthedAttr;

if(!dvauthed){ //如果没有用户认证属性数据,

sg_index–; //那就先把最后一个der当做签名数据

hr=dvseq->get_Item(sg_index,&dvsig);

if (hr!=S_OK)

_raise_error(解析数据失败。);

hr=dvsig->get_Tag(&tag);

if (tag==TAG_OPT+1) //如果最后一个der的tag是TAG_OPT+1,说明它是非

//用户认证属性数据,那读前一个der,作为签名数据

sg_index–;

}

else{

sg_index+=2; //如果有用户认证属性数据,那后面第2个就是签名数据

iCount=0;

hr=dvauthed->get_Count(&iCount);

BSTR bstype;

_bstr_t b;

for(int k=0;k

{ //遍历用户认证属性,获得正文摘要

hr=dvauthed->get_Item(k,&dvattr);

hr=dvattr->get_Tag(&tag);

if (tag==TAG_SEQUENCE)

{

hr=dvattr->get_Item(0,&dvattype);

if (hr!=S_OK)

_raise_error(获取用户认证属性数据失败。);

hr=dvattype->get_Tag(&tag);

if (tag==TAG_OBJECTID)

{

hr=dvattype->get_ObjectIdentifier(&bstype);

if(!::strcmp((_bstr_t)bstype,szOID_RSA_messageDigest))

{

hr=dvattr->get_Item(1,&dvset);

if (hr!=S_OK)

_raise_error(获取正文摘要失败。);

hr=dvset->get_Item(0,&dvdigest);

if (hr!=S_OK)

_raise_error(获取正文摘要失败。);

hr=dvdigest->get_Tag(&tag);

if (tag!=TAG_OCTETSTRING)

_raise_error(获取正文摘要数据失败,不合理的ASN1类型。);

hr=dvdigest->get_Data(&bdigest);

if (hr!=S_OK)

_raise_error(获取正文摘要数据失败。);

//这里通过一个自定义的工具类计算原文(szBuffer)摘要,具体过程就不展开

if(!co.CPCreateHash(CALG_SM3, 0, 0, &hHash))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

if (!co.CPSetHashParam(hHash,HP_SM2_PUB_KEY,(BYTE*)&hPubKey,0))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

// 对原数据进行hash运算

if(!co.CPHashData(hHash, szBuffer.GetBuffer(), szBuffer.GetSize(), 0))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

SAFEARRAY* psay;

if(S_OK!=co.GetHashData(hHash,&psay))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

if (bdigest!=psay) //计算的正文摘要和签名里获取的正文摘要

//进行对比

_raise_error(正文摘要数据不一致。);

break;

}

}

}

}

hr=dvauthed->put_Tag(TAG_SET); //获取用户认证属性数据,这里要把tag设置

//成TAG_SET,原因与构造时设置原因相同。

hr=pbAuthedAttr.get_IStream(&pStm); //因为设置tag会引起节点属性变化,造成

//get_Count方法不正常返回,所以把这段代

//码放在遍历后面执行。

_handle_result2();

hr=dvauthed->SaveBigInteger(pStm,FALSE);

_handle_result2();

hr=pbAuthedAttr.put_IStream(pStm);

_handle_result2();

}

hr=dvseq->get_Item(sg_index,&dvsig);

if (hr!=S_OK)

_raise_error(解析数据失败。);

hr=dvsig->get_Tag(&tag);

if (tag!=TAG_OCTETSTRING)

_raise_error(获取签名数据失败,不合理的ASN1类型。);

hr=dvsig->get_Data(&bsig);

if (hr!=S_OK)

_raise_error(获取签名数据失败。);

取得签名数据后,如果没有用户认证属性,则针对原文进行验证;如果有用户认证属性,则针对用户认证属性进行验证。示例代码中具体的验证过程依照国密标准,本文不展开叙述。

CMemoryLocator pbPureSignature;

hr=pbPureSignature.put_SAFEARRAY(bsig);

if (hr!=S_OK)

return hr;

if(!co.CPCreateHash(CALG_SM2, 0, 0, &hHash))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

if (!co.CPSetHashParam(hHash,HP_SM2_PUB_KEY,(BYTE*)&hPubKey,0))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

if(!dvauthed) //如果没有用户认证属性,则直接用原文摘要值验证

{

if(!co.CPHashData(hHash, szBuffer.GetBuffer(), szBuffer.GetSize(), 0))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

}

else{ //如果有用户认证属性,则用用户认证属性摘要值验证

if(!co.CPHashData(hHash, pbAuthedAttr.GetBuffer(), pbAuthedAttr.GetSize(), 0))

return MAKE_HRESULT2(1,FACILITY_WIN32,::GetLastError());

}

BSTR bs;

pbAuthedAttr.Base64Encode(&bs);

BSTR bs2;

pbPureSignature.Base64Encode(&bs2);

LPTSTR szDescription=NULL;

// 验证数字签名

BOOLEAN verifyResult=FALSE;

if(!co.CPVerifySignature(hHash, pbPureSignature, pbPureSignature.GetSize(), hPubKey, szDescription, 0))

{

_raise_error(数字签名验证失败。);

}

你可能感兴趣的:(如何构造PKCS7签名(三))