我一直在使用以下代码(取自
KB323809 article)来检索有关可执行文件上的代码签名的信息.这适用于单个数字签名.
但是如何检索多个代码签名的信息?
在这种情况下,下面的Microsoft代码只是为第一个签名重新获取信息.
我的想法是用CMSG_SIGNER_COUNT_PARAM调用CryptMsgGetParam来获取签名的数量,然后将每个签名索引传递给随后使用CMSG_SIGNER_INFO_PARAM调用CryptMsgGetParam(在下面的代码中).但是这种方法总是返回1个签名,即使我显然有更多,像这个例子中的3:
#include <windows.h> #include <wincrypt.h> #include <wintrust.h> #include <stdio.h> #include <tchar.h> #pragma comment(lib,"crypt32.lib") #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) typedef struct { LPWSTR lpszProgramName; LPWSTR lpszPublisherLink; LPWSTR lpszMoreInfoLink; } SPROG_PUBLISHERINFO,*PSPROG_PUBLISHERINFO; BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,PSPROG_PUBLISHERINFO Info); BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo,SYSTEMTIME *st); BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext); BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,PCMSG_SIGNER_INFO *pCounterSignerInfo); int _tmain(int argc,TCHAR *argv[]) { WCHAR szFileName[MAX_PATH]; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; PCCERT_CONTEXT pCertContext = NULL; BOOL fResult; DWORD dwEncoding,dwContentType,dwFormatType; PCMSG_SIGNER_INFO pSignerInfo = NULL; PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; DWORD dwSignerInfo; CERT_INFO CertInfo; SPROG_PUBLISHERINFO ProgPubInfo; SYSTEMTIME st; ZeroMemory(&ProgPubInfo,sizeof(ProgPubInfo)); __try { if (argc != 2) { _tprintf(_T("Usage: SignedFileInfo <filename>\n")); return 0; } #ifdef UNICODE lstrcpynW(szFileName,argv[1],MAX_PATH); #else if (mbstowcs(szFileName,MAX_PATH) == -1) { printf("Unable to convert to unicode.\n"); __leave; } #endif // Get message handle and store handle from the signed file. fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,szFileName,CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,CERT_QUERY_FORMAT_FLAG_BINARY,&dwEncoding,&dwContentType,&dwFormatType,&hStore,&hMsg,NULL); if (!fResult) { _tprintf(_T("CryptQueryObject Failed with %x\n"),GetLastError()); __leave; } // Get signer information size. fResult = CryptMsgGetParam(hMsg,CMSG_SIGNER_INFO_PARAM,NULL,&dwSignerInfo); if (!fResult) { _tprintf(_T("CryptMsgGetParam Failed with %x\n"),GetLastError()); __leave; } // Allocate memory for signer information. pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR,dwSignerInfo); if (!pSignerInfo) { _tprintf(_T("Unable to allocate memory for Signer Info.\n")); __leave; } // Get Signer Information. fResult = CryptMsgGetParam(hMsg,(PVOID)pSignerInfo,GetLastError()); __leave; } // Get program name and publisher information from // signer info structure. if (GetProgAndPublisherInfo(pSignerInfo,&ProgPubInfo)) { if (ProgPubInfo.lpszProgramName != NULL) { wprintf(L"Program Name : %s\n",ProgPubInfo.lpszProgramName); } if (ProgPubInfo.lpszPublisherLink != NULL) { wprintf(L"Publisher Link : %s\n",ProgPubInfo.lpszPublisherLink); } if (ProgPubInfo.lpszMoreInfoLink != NULL) { wprintf(L"MoreInfo Link : %s\n",ProgPubInfo.lpszMoreInfoLink); } } _tprintf(_T("\n")); // Search for the signer certificate in the temporary // certificate store. CertInfo.Issuer = pSignerInfo->Issuer; CertInfo.SerialNumber = pSignerInfo->SerialNumber; pCertContext = CertFindCertificateInStore(hStore,ENCODING,CERT_FIND_SUBJECT_CERT,(PVOID)&CertInfo,NULL); if (!pCertContext) { _tprintf(_T("CertFindCertificateInStore Failed with %x\n"),GetLastError()); __leave; } // Print Signer certificate information. _tprintf(_T("Signer Certificate:\n\n")); PrintCertificateInfo(pCertContext); _tprintf(_T("\n")); // Get the timestamp certificate signerinfo structure. if (GetTimeStampSignerInfo(pSignerInfo,&pCounterSignerInfo)) { // Search for Timestamp certificate in the temporary // certificate store. CertInfo.Issuer = pCounterSignerInfo->Issuer; CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber; pCertContext = CertFindCertificateInStore(hStore,NULL); if (!pCertContext) { _tprintf(_T("CertFindCertificateInStore Failed with %x\n"),GetLastError()); __leave; } // Print timestamp certificate information. _tprintf(_T("TimeStamp Certificate:\n\n")); PrintCertificateInfo(pCertContext); _tprintf(_T("\n")); // Find Date of timestamp. if (GetDateOfTimeStamp(pCounterSignerInfo,&st)) { _tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),st.wMonth,st.wDay,st.wYear,st.wHour,st.wMinute); } _tprintf(_T("\n")); } } __finally { // Clean up. if (ProgPubInfo.lpszProgramName != NULL) LocalFree(ProgPubInfo.lpszProgramName); if (ProgPubInfo.lpszPublisherLink != NULL) LocalFree(ProgPubInfo.lpszPublisherLink); if (ProgPubInfo.lpszMoreInfoLink != NULL) LocalFree(ProgPubInfo.lpszMoreInfoLink); if (pSignerInfo != NULL) LocalFree(pSignerInfo); if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo); if (pCertContext != NULL) CertFreeCertificateContext(pCertContext); if (hStore != NULL) CertCloseStore(hStore,0); if (hMsg != NULL) CryptMsgClose(hMsg); } return 0; } BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext) { BOOL fReturn = FALSE; LPTSTR szName = NULL; DWORD dwData; __try { // Print Serial Number. _tprintf(_T("Serial Number: ")); dwData = pCertContext->pCertInfo->SerialNumber.cbData; for (DWORD n = 0; n < dwData; n++) { _tprintf(_T("%02x "),pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]); } _tprintf(_T("\n")); // Get Issuer name size. if (!(dwData = CertGetNameString(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,CERT_NAME_ISSUER_FLAG,0))) { _tprintf(_T("CertGetNameString Failed.\n")); __leave; } // Allocate memory for Issuer name. szName = (LPTSTR)LocalAlloc(LPTR,dwData * sizeof(TCHAR)); if (!szName) { _tprintf(_T("Unable to allocate memory for issuer name.\n")); __leave; } // Get Issuer name. if (!(CertGetNameString(pCertContext,szName,dwData))) { _tprintf(_T("CertGetNameString Failed.\n")); __leave; } // print Issuer name. _tprintf(_T("Issuer Name: %s\n"),szName); LocalFree(szName); szName = NULL; // Get Subject name size. if (!(dwData = CertGetNameString(pCertContext,0))) { _tprintf(_T("CertGetNameString Failed.\n")); __leave; } // Allocate memory for subject name. szName = (LPTSTR)LocalAlloc(LPTR,dwData * sizeof(TCHAR)); if (!szName) { _tprintf(_T("Unable to allocate memory for subject name.\n")); __leave; } // Get subject name. if (!(CertGetNameString(pCertContext,dwData))) { _tprintf(_T("CertGetNameString Failed.\n")); __leave; } // Print Subject Name. _tprintf(_T("Subject Name: %s\n"),szName); fReturn = TRUE; } __finally { if (szName != NULL) LocalFree(szName); } return fReturn; } LPWSTR AllocateAndCopyWideString(LPCWSTR inputString) { LPWSTR outputString = NULL; outputString = (LPWSTR)LocalAlloc(LPTR,(wcslen(inputString) + 1) * sizeof(WCHAR)); if (outputString != NULL) { lstrcpyW(outputString,inputString); } return outputString; } BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,PSPROG_PUBLISHERINFO Info) { BOOL fReturn = FALSE; PSPC_SP_OPUS_INFO OpusInfo = NULL; DWORD dwData; BOOL fResult; __try { // Loop through authenticated attributes and find // SPC_SP_OPUS_INFO_OBJID OID. for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { // Get Size of SPC_SP_OPUS_INFO structure. fResult = CryptDecodeObject(ENCODING,SPC_SP_OPUS_INFO_OBJID,pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,&dwData); if (!fResult) { _tprintf(_T("CryptDecodeObject Failed with %x\n"),GetLastError()); __leave; } // Allocate memory for SPC_SP_OPUS_INFO structure. OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR,dwData); if (!OpusInfo) { _tprintf(_T("Unable to allocate memory for Publisher Info.\n")); __leave; } // Decode and get SPC_SP_OPUS_INFO structure. fResult = CryptDecodeObject(ENCODING,OpusInfo,GetLastError()); __leave; } // Fill in Program Name if present. if (OpusInfo->pwszProgramName) { Info->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName); } else Info->lpszProgramName = NULL; // Fill in Publisher Information if present. if (OpusInfo->pPublisherInfo) { switch (OpusInfo->pPublisherInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile); break; default: Info->lpszPublisherLink = NULL; break; } } else { Info->lpszPublisherLink = NULL; } // Fill in More Info if present. if (OpusInfo->pMoreInfo) { switch (OpusInfo->pMoreInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile); break; default: Info->lpszMoreInfoLink = NULL; break; } } else { Info->lpszMoreInfoLink = NULL; } fReturn = TRUE; break; // Break from for loop. } // lstrcmp SPC_SP_OPUS_INFO_OBJID } // for } __finally { if (OpusInfo != NULL) LocalFree(OpusInfo); } return fReturn; } BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo,SYSTEMTIME *st) { BOOL fResult; FILETIME lft,ft; DWORD dwData; BOOL fReturn = FALSE; // Loop through authenticated attributes and find // szOID_RSA_signingTime OID. for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(szOID_RSA_signingTime,pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { // Decode and get FILETIME structure. dwData = sizeof(ft); fResult = CryptDecodeObject(ENCODING,szOID_RSA_signingTime,(PVOID)&ft,&dwData); if (!fResult) { _tprintf(_T("CryptDecodeObject Failed with %x\n"),GetLastError()); break; } // Convert to local time. FileTimeToLocalFileTime(&ft,&lft); FileTimeToSystemTime(&lft,st); fReturn = TRUE; break; // Break from for loop. } //lstrcmp szOID_RSA_signingTime } // for return fReturn; } BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,PCMSG_SIGNER_INFO *pCounterSignerInfo) { PCCERT_CONTEXT pCertContext = NULL; BOOL fReturn = FALSE; BOOL fResult; DWORD dwSize; __try { *pCounterSignerInfo = NULL; // Loop through unathenticated attributes for // szOID_RSA_counterSign OID. for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++) { if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,szOID_RSA_counterSign) == 0) { // Get size of CMSG_SIGNER_INFO structure. fResult = CryptDecodeObject(ENCODING,PKCS7_SIGNER_INFO,pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,&dwSize); if (!fResult) { _tprintf(_T("CryptDecodeObject Failed with %x\n"),GetLastError()); __leave; } // Allocate memory for CMSG_SIGNER_INFO. *pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR,dwSize); if (!*pCounterSignerInfo) { _tprintf(_T("Unable to allocate memory for timestamp info.\n")); __leave; } // Decode and get CMSG_SIGNER_INFO structure // for timestamp certificate. fResult = CryptDecodeObject(ENCODING,(PVOID)*pCounterSignerInfo,GetLastError()); __leave; } fReturn = TRUE; break; // Break from for loop. } } } __finally { // Clean up. if (pCertContext != NULL) CertFreeCertificateContext(pCertContext); } return fReturn; }
解决方法
除了Daniel Sie的答案.
找到一个包含CMSG_SIGNER_INFO的属性(szOID_NESTED_SIGNATURE),需要通过以下步骤进行解码(这是Delphi代码,但感觉很清楚):
LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,nil,0); CryptMsgUpdate(LNestedMsg,LFindedAttr.rgValue.pbData,LFindedAttr.rgValue.cbData,True); CryptMsgGetParam(LNestedMsg,@LSize); CryptMsgGetParam(LNestedMsg,LNestedSignerInfo,@LSize);
获取的CMSG_SIGNER_INFO(LNestedSignerInfo)是嵌套签名(在我们的示例中为SHA2签名).
要获取该签名的时间戳信息(RFC3161),请使用pszObjId = szOID_RFC3161_counterSign(1.3.6.1.4.1.311.3.3.1)搜索Unauthenticated属性.
Found属性将包含时间戳计数器签名的CMSG_SIGNER_INFO,还需要通过先前描述的步骤(CryptMsgOpenToDecode,CryptMsgUpdate,CryptMsgGetParam)进行解码.
嵌套签名或时间戳计数器签名的CERT_CONTEXT需要在存储中搜索,并从相应的HCRYPTMSG(CryptMsgOpenToDecode的结果)获得.
LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG,PKCS_7_ASN_ENCODING or X509_ASN_ENCODING,LNestedMsg); LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG,LTimeStampMsg);
Example to decode szOID_RFC3161_counterSign attribute:
LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr; for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do begin if SameText(string(LNestedSignerAttr.pszObjId),szOID_RFC3161_counterSign) then begin LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,nil); if not Assigned(LNestedTimeStampMsg) then RaiseLastOSError; try if not CryptMsgUpdate(LNestedTimeStampMsg,LNestedSignerAttr.rgValue.pbData,LNestedSignerAttr.rgValue.cbData,True) then RaiseLastOSError; if not CryptMsgGetParam(LNestedTimeStampMsg,@LSize) then RaiseLastOSError; GetMem(LTimeStampSigner,LSize); try if not CryptMsgGetParam(LNestedTimeStampMsg,LTimeStampSigner,@LSize) then RaiseLastOSError; LAttr := LTimeStampSigner.AuthAttrs.rgAttr; for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do begin if SameText(string(LAttr.pszObjId),szOID_RSA_signingTime) then begin LSize := SizeOf(LFileTime); if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,LAttr.rgValue.pbData,LAttr.rgValue.cbData,@LFileTime,@LSize) then RaiseLastOSError; if FileTimeToLocalFileTime(@LFileTime,LLocalFileTime) and FileTimeToSystemTime(@LLocalFileTime,LSystemTime) then SHA2TimeStamp := SystemTimeToDateTime(LSystemTime) else SHA2TimeStamp := 0; end; Inc(LAttr); end; finally FreeMem(LTimeStampSigner); end; finally if not CryptMsgClose(LNestedTimeStampMsg) then RaiseLastOSError; end; end; Inc(LNestedSignerAttr); end;