我一直在努力修改Microsoft的this code sample,以显示(有些过时)如何从可执行文件中检索代码签名信息的方式。它可以工作,但是如果二进制文件是双重签名的,则不会检索信息。
因此,我做了一些研究,并试图对其进行重写,以使其能够识别Windows中许多现代可执行文件中存在的双重签名。不幸的是,几乎没有(模糊的)建议可用(1)(2),例如那些使用UnauthenticatedAttributes
和szOID_NESTED_SIGNATURE
的建议(无论如何),但仅用于检索时间戳。
所以我试图重写该Microsoft代码,这就是我得到的:
(要构建它,只需将其复制到Visual Studio控制台项目中并更改.exe文件路径。用于处理双重签名的代码在PrintDualSignatureInfo()
函数中。) < / p>
#include "stdafx.h"
//SOURCE:
// https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
//
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#include <atlconv.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);
void PrintSignatureAlgorithm(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo);
void PrintDualSignatureInfo(PCMSG_SIGNER_INFO pSignerInfo);
void PrintCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertName);
int main()
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
DWORD dwSignerInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
LPCTSTR pExePath = L"C:\\Users\\UserName\\Downloads\\procmon.exe"; //works
//pExePath = L"C:\\Users\\UserName\\Downloads\\putty.exe"; //doesnt work
lstrcpynW(szFileName, pExePath, MAX_PATH);
// 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,
0,
&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,
0,
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,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), 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"));
// Print Signer certificate information.
PrintCertificateInfo(hStore, pSignerInfo, L"Signer Certificate");
//Look for dual signature
PrintDualSignatureInfo(pSignerInfo);
}
__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 (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}
void PrintCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertName)
{
if(hStore &&
pSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
CERT_INFO CertInfo = {0};
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
SYSTEMTIME st;
__try
{
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(L"%s:\n\n", pStrCertName); //(_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,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
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
{
if (pCounterSignerInfo != NULL)
LocalFree(pCounterSignerInfo);
if (pCertContext != NULL)
CertFreeCertificateContext(pCertContext);
}
}
}
void PrintDualSignatureInfo(PCMSG_SIGNER_INFO pSignerInfo)
{
if(pSignerInfo)
{
for(DWORD a = 0; a < pSignerInfo->UnauthAttrs.cAttr; a++)
{
if(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId &&
lstrcmpiA(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId, szOID_NESTED_SIGNATURE) == 0)
{
HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, NULL, NULL, NULL);
if(hMsg)
{
if(::CryptMsgUpdate(hMsg,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData,
TRUE))
{
DWORD dwSignerInfo = 0;
::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo);
if(dwSignerInfo != 0)
{
PCMSG_SIGNER_INFO pSignerInfo2 = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwSignerInfo];
if(pSignerInfo2)
{
if(::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM,
0, (PVOID)pSignerInfo2, &dwSignerInfo))
{
CRYPT_DATA_BLOB p7Data;
p7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData;
p7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData;
HCERTSTORE hStore = ::CertOpenStore(CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, &p7Data);
if(hStore)
{
// Print Signer certificate information.
PrintCertificateInfo(hStore, pSignerInfo2, L"Dual Signer Certificate");
//Close
::CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
}
}
//Free mem
delete[] pSignerInfo2;
pSignerInfo2 = NULL;
}
}
}
//Close message
::CryptMsgClose(hMsg);
}
}
}
}
}
void PrintSignatureAlgorithm(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo)
{
if(pSigAlgo &&
pSigAlgo->pszObjId)
{
PCCRYPT_OID_INFO pCOI = ::CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSigAlgo->pszObjId, 0);
if(pCOI &&
pCOI->pwszName)
{
_tprintf(L"%s", pCOI->pwszName);
}
else
{
USES_CONVERSION;
_tprintf(L"%s", A2W(pSigAlgo->pszObjId));
}
}
}
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"));
//Hashing algoriths
_tprintf(L"Signature Algorithm: ");
PrintSignatureAlgorithm(&pCertContext->pCertInfo->SignatureAlgorithm);
_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
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,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
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,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
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,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
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,
0,
NULL,
&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,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
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,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(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,
0,
NULL,
&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,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
不幸的是,在处理双重签名时,我的想法并不总是能正常工作。
例如,工作示例。如果我在Sysiternal的ProcMon上运行它,它将具有双重签名,正如我们在Windows资源管理器中看到的那样:
我的代码正确检索了SHA1
和SHA256
签名:
但是,这是一个无效示例。如果我在其他双签名文件上运行它,请说Putty可执行文件,它也具有双签名:
上面的代码两次检索相同的SHA256
证书:
知道为什么吗?
PS。这不仅发生在Putty的签名上。还有其他具有相同行为的双签名可执行文件。
答案 0 :(得分:2)
输出中的摘要算法与文件属性中显示的摘要算法不匹配的原因是,您在输出中显示的是签名证书链中第一个证书的摘要算法,而不是摘要算法Authenticode签名本身。就像这样:
+-----------+ +-------------------+ +---------+ | Root Cert | signs | Intermediate Cert | signs | PE Data | |-----------|=========>|-------------------|=========>|---------| | SHA256 | | SHA256 | | SHA1 | +-----------+ +-------------------+ +---------+ ^ ^ | | You are showing But you this want to show this
Authenticode的工作方式是,首先使用有时被Microsoft称为“摘要算法”的内容来计算文件摘要。然后使用提供其证书的签名密钥对该摘要进行签名。但是,该证书本身是通过使用所谓的“签名摘要”计算其摘要并使用来自证书链中较高级别的证书的密钥对其进行签名的,等等。
可以使用CertFindCertificateInStore
函数获得证书链中的第一个证书。然后,您应该在while循环中继续调用CertFindCertificateInStore
以获取其他证书。您在代码中所做的就是获取证书链中的第一个证书(使用CertFindCertificateInStore
)并打印其签名摘要算法。相反,您要做的是获取并打印用于文件签名的摘要算法。您可以使用带有CryptMsgGetParam
标志的CMSG_SIGNER_INFO_PARAM
来做到这一点,并获得它,只是不打印它。
考虑这一点的另一种方式是根据MSG_SIGNER_INFO
和CERT_INFO
之间的关系。这不是信息需要匹配的1-1关系。更像是:
+---------------+ +---------------+ | SIGNER_INFO 1 | | SIGNER_INFO 2 | |---------------| |---------------| | SHA1 | | SHA256 | +---------------+ +---------------+ | | | +-------------+ | +-------------+ +---| CERT_INFO 1 | +---| CERT_INFO 3 | |-------------| |-------------| | SHA256 | | SHA256 | +-------------+ +-------------+ | | +-------------+ +-------------+ | CERT_INFO 2 | | CERT_INFO 4 | |-------------| |-------------| | SHA1 | | SHA1 | +-------------+ +-------------+
答案 1 :(得分:1)
我想我可以使用它
这是更新的代码。请确保在使用前先检查一下,如果有任何问题,请发表评论:
#include <new>
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#include <atlconv.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
enum RESULT_FIND_CONTEXT{
RFC_FOUND_CONTEXT,
RFC_NO_CONTEXT,
};
enum RESULT_FIND_CERT_STORE{
RFCS_ERROR = -1,
RFCS_NONE = 0,
RFCS_FOUND_ONE = 1,
};
void RetrieveDigitalSignatureInfo(const WCHAR* pFilePath);
void PrintProgramAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo);
RESULT_FIND_CONTEXT PrintCertificateInformation(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc = NULL);
void PrintCertContextDetails(PCCERT_CONTEXT pCertContext, DWORD dwNameOutputType, CRYPT_ALGORITHM_IDENTIFIER* pHashAlgo);
void PrintDigestAlgorithmName(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo);
BOOL PrintSignerDateTime(FILETIME* pftUtc);
int PrintSignerTimeStampDateTime(PCMSG_SIGNER_INFO pSignerInfo);
RESULT_FIND_CERT_STORE FindCertStoreByIndex(int iIndex, HCERTSTORE& hOutStore, CRYPT_DATA_BLOB* p7Data = NULL);
void PrintDualSignatureInformation(PCMSG_SIGNER_INFO pSignerInfo);
void FindAppropriateStoreAndPrintCertificateInformation(PCMSG_SIGNER_INFO pSignerInfo, CRYPT_DATA_BLOB* p7Data, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc = NULL);
int _tmain(int argc, WCHAR* argv[])
{
LPCTSTR pExePath;
if(argc <= 1)
{
pExePath = L"C:\\Users\\UserName\\Downloads\\procmon.exe";
//pExePath = L"C:\\Users\\UserName\\Downloads\\putty.exe";
}
else
{
//Otherwise use first argument from command line
pExePath = argv[1];
}
_tprintf(L"File: %s\n", pExePath);
RetrieveDigitalSignatureInfo(pExePath);
return 0;
}
//The following functions were re-written from the following source to be able to
//retrieve dual-signatures from PE binaries:
//
// https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
void RetrieveDigitalSignatureInfo(const WCHAR* pFilePath)
{
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
if(CryptQueryObject(CERT_QUERY_OBJECT_FILE,
pFilePath,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
NULL,
NULL,
NULL,
&hStore,
&hMsg,
NULL))
{
//We must have at least one signer
DWORD dwCountSigners = 0;
DWORD dwcbSz = sizeof(dwCountSigners);
if(CryptMsgGetParam(hMsg, CMSG_SIGNER_COUNT_PARAM, 0, &dwCountSigners, &dwcbSz))
{
if(dwCountSigners != 0)
{
//Get Signer Information
dwcbSz = 0;
CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwcbSz);
if(dwcbSz)
{
PCMSG_SIGNER_INFO pSignerInfo = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwcbSz];
if(pSignerInfo)
{
DWORD dwcbSz2 = dwcbSz;
if(CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, pSignerInfo, &dwcbSz) &&
dwcbSz == dwcbSz2)
{
//Print program publisher info
PrintProgramAndPublisherInfo(pSignerInfo);
_tprintf(L"\n");
//Print signer certificate info
if(PrintCertificateInformation(hStore, pSignerInfo, L"Signer Certificate", FALSE) == RFC_NO_CONTEXT)
{
_tprintf(L"ERROR: (0x%X) CertFindCertificateInStore(CERT_FIND_SUBJECT_CERT) data failed\n", ::GetLastError());
}
//Print dual-signature info
PrintDualSignatureInformation(pSignerInfo);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pSignerInfo;
pSignerInfo = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(PCMSG_SIGNER_INFO) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: Must have to least one signer\n");
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_COUNT_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptQueryObject(CERT_QUERY_OBJECT_FILE) failed\n", ::GetLastError());
//Clear up
if (hStore != NULL)
{
CertCloseStore(hStore, 0);
hStore = NULL;
}
if (hMsg != NULL)
{
CryptMsgClose(hMsg);
hMsg = NULL;
}
}
void PrintProgramAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo)
{
// Loop through authenticated attributes and find SPC_SP_OPUS_INFO_OBJID OID.
for(DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId)
{
if(lstrcmpA(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId, SPC_SP_OPUS_INFO_OBJID) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
PSPC_SP_OPUS_INFO pInfo = NULL;
DWORD dwcbSz = 0;
if(CryptDecodeObjectEx(ENCODING, SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pInfo, &dwcbSz) &&
pInfo &&
dwcbSz)
{
if(pInfo->pwszProgramName)
{
_tprintf(L"Program Name: %s\n", pInfo->pwszProgramName);
}
if (pInfo->pPublisherInfo)
{
switch (pInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
_tprintf(L"Publisher Link: %s\n", pInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
_tprintf(L"Publisher Link: %s\n", pInfo->pPublisherInfo->pwszFile);
break;
}
}
if (pInfo->pMoreInfo)
{
switch (pInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
_tprintf(L"MoreInfo Link: %s\n", pInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
_tprintf(L"MoreInfo Link: %s\n", pInfo->pMoreInfo->pwszFile);
break;
}
}
}
else
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(SPC_SP_OPUS_INFO_OBJID) data failed\n", ::GetLastError());
if(pInfo)
{
::LocalFree(pInfo);
pInfo = NULL;
}
}
}
}
}
RESULT_FIND_CONTEXT PrintCertificateInformation(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc)
{
CERT_INFO ci = {0};
ci.Issuer = pSignerInfo->Issuer;
ci.SerialNumber = pSignerInfo->SerialNumber;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwcbSz;
int c = 0;
for(;; c++)
{
//Enumerate and look for needed cert context
pCertContext = CertFindCertificateInStore(hStore,
ENCODING, 0, CERT_FIND_SUBJECT_CERT,
(PVOID)&ci, pCertContext);
if(!pCertContext)
{
break;
}
if(!c)
{
//Print Signer certificate information.
_tprintf(L"%s:\n", pStrCertDescription);
if(!bIsTimeStamp)
{
_tprintf(L"----------------\n");
}
_tprintf(L"\n");
}
//In case of a timestamp
if(bIsTimeStamp)
{
//Print time stamp
if(!pftTimeStampUtc)
{
//Retrieve and print it
if(PrintSignerTimeStampDateTime(pSignerInfo) == 0)
{
_tprintf(L"ERROR: (0x%X) Failed to retrieve date/time for TimeStamp\n", ::GetLastError());
}
}
else
{
//We have a time-stamp already
if(!PrintSignerDateTime(pftTimeStampUtc))
{
_tprintf(L"ERROR: (0x%X) Time conversion failed from %I64x\n", ::GetLastError(), *(ULONGLONG*)pftTimeStampUtc);
}
}
}
//Print subject name, issuer name, serial, signature algorithm
PrintCertContextDetails(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE, //Or use CERT_NAME_RDN_TYPE for a more detailed output
&pSignerInfo->HashAlgorithm);
volatile static char pmsgDoNotCopyAsIs[] =
"Please read & verify this code before you "
"copy-and-paste it into your production project! "
"https://stackoverflow.com/q/50976612/3170929 "
"{438EE426-7131-4498-8AF7-9DDCB2508F0C}";
srand(rand()^pmsgDoNotCopyAsIs[0]);
_tprintf(L"\n");
#ifndef szOID_RFC3161_counterSign
#define szOID_RFC3161_counterSign "1.3.6.1.4.1.311.3.3.1"
#endif
if(!bIsTimeStamp)
{
//Get time stamp certificate(s)
//Loop through unathenticated attributes and look for specific OIDs
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId)
{
if(lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign) == 0)
{
//This is a legacy signature standard
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
dwcbSz = 0;
if(CryptDecodeObjectEx(ENCODING, PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pCounterSignerInfo, &dwcbSz) &&
pCounterSignerInfo &&
dwcbSz)
{
//Got one signature
if(PrintCertificateInformation(hStore, pCounterSignerInfo, L"TimeStamp Certificate", TRUE) == RFC_NO_CONTEXT)
{
_tprintf(L"ERROR: (0x%X) CertFindCertificateInStore(CERT_FIND_SUBJECT_CERT) data failed\n", ::GetLastError());
}
}
else
{
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(PKCS7_SIGNER_INFO) failed\n", ::GetLastError());
}
//Free mem
if(pCounterSignerInfo)
{
::LocalFree(pCounterSignerInfo);
pCounterSignerInfo = NULL;
}
}
else if(lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RFC3161_counterSign) == 0)
{
//Using an RFC3161 time stamp
if(pSignerInfo->UnauthAttrs.rgAttr[n].cValue != 0)
{
HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(ENCODING, 0, 0, NULL, NULL, NULL);
if(hMsg)
{
if(::CryptMsgUpdate(hMsg,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->cbData,
TRUE))
{
dwcbSz = 0;
::CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &dwcbSz);
if(dwcbSz != 0)
{
BYTE* pCntData = new (std::nothrow) BYTE[dwcbSz];
if(pCntData)
{
if(::CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pCntData, &dwcbSz))
{
//Retrieve time stamp
FILETIME ftUtc = {0};
void* pTmData = NULL;
DWORD dwcbTmDataSz = 0;
struct Microsoft_forgot_to_document_me{
void* something_0[9];
FILETIME ftUtc;
};
#ifndef TIMESTAMP_INFO
#define TIMESTAMP_INFO ((LPCSTR) 80)
#endif
if(CryptDecodeObjectEx(ENCODING, //X509_ASN_ENCODING,
TIMESTAMP_INFO,
pCntData,
dwcbSz,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pTmData, &dwcbTmDataSz) &&
pTmData &&
dwcbTmDataSz >= sizeof(Microsoft_forgot_to_document_me))
{
ftUtc = ((Microsoft_forgot_to_document_me*)pTmData)->ftUtc;
}
else
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(RFC3161/TIMESTAMP_INFO) data failed\n", ::GetLastError());
if(pTmData)
{
::LocalFree(pTmData);
pTmData = NULL;
}
//Try to get signer info
dwcbSz = 0;
::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwcbSz);
if(dwcbSz != 0)
{
CMSG_SIGNER_INFO* pTmSignerData = (CMSG_SIGNER_INFO*)new (std::nothrow) BYTE[dwcbSz];
if(pTmSignerData)
{
if(::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, pTmSignerData, &dwcbSz))
{
CRYPT_DATA_BLOB c7Data;
c7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->pbData;
c7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->cbData;
//Try to locate the appropriate store
FindAppropriateStoreAndPrintCertificateInformation(pTmSignerData, &c7Data, L"TimeStamp Certificate", TRUE, &ftUtc);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pTmSignerData;
pTmSignerData = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(RFC3161) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_CONTENT_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pCntData;
pCntData = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(RFC3161) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_CONTENT_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgUpdate(RFC3161) failed\n", ::GetLastError());
//Free handle
::CryptMsgClose(hMsg);
hMsg = NULL;
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgOpenToDecode(ENCODING) failed\n", ::GetLastError());
}
}
}
}
}
}
//Free
if(pCertContext)
{
CertFreeCertificateContext(pCertContext);
pCertContext = NULL;
}
return c != 0 ? RFC_FOUND_CONTEXT : RFC_NO_CONTEXT;
}
void PrintCertContextDetails(PCCERT_CONTEXT pCertContext, DWORD dwNameOutputType, CRYPT_ALGORITHM_IDENTIFIER* pHashAlgo)
{
//'dwNameOutputType' = one of: CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_RDN_TYPE, etc. see CertGetNameString()
DWORD dwcbSz;
WCHAR* pBuff;
//Get subject name.
dwcbSz = CertGetNameString(pCertContext, dwNameOutputType, 0, NULL, NULL, 0);
if(dwcbSz != 0)
{
pBuff = new (std::nothrow) WCHAR[dwcbSz];
if(pBuff)
{
if(CertGetNameString(pCertContext, dwNameOutputType, 0, NULL, pBuff, dwcbSz) == dwcbSz)
{
_tprintf(L"Subject: %s\n", pBuff);
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(subject) data failed\n", ::GetLastError());
//Free mem
delete[] pBuff;
pBuff = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new CertGetNameString(subject) data failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(subject) failed\n", ::GetLastError());
//Issuer
dwcbSz = CertGetNameString(pCertContext, dwNameOutputType, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
if(dwcbSz != 0)
{
pBuff = new (std::nothrow) WCHAR[dwcbSz];
if(pBuff)
{
if(CertGetNameString(pCertContext, dwNameOutputType, CERT_NAME_ISSUER_FLAG, NULL, pBuff, dwcbSz) == dwcbSz)
{
_tprintf(L"Issuer: %s\n", pBuff);
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(issuer) data failed\n", ::GetLastError());
//Free mem
delete[] pBuff;
pBuff = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new CertGetNameString(issuer) data failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(issuer) failed\n", ::GetLastError());
//Print Serial Number.
_tprintf(_T("Serial Number: "));
dwcbSz = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwcbSz; n++)
{
_tprintf(_T("%02x"), pCertContext->pCertInfo->SerialNumber.pbData[dwcbSz - (n + 1)]);
}
_tprintf(_T("\n"));
//Digest algorithm
_tprintf(L"Digest Algorithm: ");
PrintDigestAlgorithmName(pHashAlgo);
_tprintf(_T("\n"));
}
void PrintDigestAlgorithmName(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo)
{
if(pSigAlgo &&
pSigAlgo->pszObjId)
{
PCCRYPT_OID_INFO pCOI = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSigAlgo->pszObjId, 0);
if(pCOI &&
pCOI->pwszName)
{
_tprintf(L"%s", pCOI->pwszName);
}
else
{
USES_CONVERSION;
_tprintf(L"%s", A2W(pSigAlgo->pszObjId));
}
}
}
BOOL PrintSignerDateTime(FILETIME* pftUtc)
{
BOOL bRes = FALSE;
if(pftUtc)
{
//Convert to local time
FILETIME ftLoc = {0};
SYSTEMTIME stLoc = {0};
if(FileTimeToLocalFileTime(pftUtc, &ftLoc) &&
FileTimeToSystemTime(&ftLoc, &stLoc))
{
_tprintf(L"Date of TimeStamp : %02d/%02d/%04d %02d:%02d:%02d\n",
stLoc.wMonth,
stLoc.wDay,
stLoc.wYear,
stLoc.wHour,
stLoc.wMinute,
stLoc.wSecond);
bRes = TRUE;
}
}
else
::SetLastError(ERROR_INVALID_PARAMETER);
return bRes;
}
int PrintSignerTimeStampDateTime(PCMSG_SIGNER_INFO pSignerInfo)
{
int nCountTimeStamps = 0;
//Loop through authenticated attributes
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (pSignerInfo->AuthAttrs.rgAttr[n].pszObjId &&
lstrcmpA(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId, szOID_RSA_signingTime) == 0)
{
// Decode and get FILETIME structure.
FILETIME ftUtc = {0};
DWORD dwData = sizeof(ftUtc);
if(CryptDecodeObject(ENCODING, PKCS_UTC_TIME,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ftUtc, &dwData))
{
//Got time stamp
nCountTimeStamps++;
//And print it
if(!PrintSignerDateTime(&ftUtc))
{
_tprintf(L"ERROR: (0x%X) Time conversion failed from %I64x\n", ::GetLastError(), *(ULONGLONG*)&ftUtc);
}
}
else
{
_tprintf(L"ERROR: (0x%X) CryptDecodeObject(PKCS_UTC_TIME) failed\n", ::GetLastError());
}
}
}
return nCountTimeStamps;
}
RESULT_FIND_CERT_STORE FindCertStoreByIndex(int iIndex, HCERTSTORE& hOutStore, CRYPT_DATA_BLOB* p7Data)
{
//'hOutStore' = receives cert store handle. If not NULL, make sure to release it by calling CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
//'p7Data' = used with index 0 only
hOutStore = NULL;
switch (iIndex)
{
case 0:
hOutStore = CertOpenStore(CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, p7Data);
break;
case 1:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"ROOT");
break;
case 2:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"TRUST");
break;
case 3:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"CA");
break;
case 4:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"MY");
break;
case 5:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x20000, // flags = 0x28001
"SPC");
break;
default:
return RFCS_NONE;
}
return hOutStore ? RFCS_FOUND_ONE : RFCS_ERROR;
}
void FindAppropriateStoreAndPrintCertificateInformation(PCMSG_SIGNER_INFO pSignerInfo, CRYPT_DATA_BLOB* p7Data, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc)
{
HCERTSTORE hStore = NULL;
//Try to locate the appropriate store
for(int i = 0;; i++)
{
if(hStore)
{
CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
hStore = NULL;
}
RESULT_FIND_CERT_STORE resFnd = FindCertStoreByIndex(i, hStore, p7Data);
if(resFnd == RFCS_FOUND_ONE)
{
//Try to retrieve info
if(PrintCertificateInformation(hStore, pSignerInfo, pStrCertDescription, bIsTimeStamp, pftTimeStampUtc) == RFC_FOUND_CONTEXT)
{
//All done
break;
}
}
else
{
//Stop the seatch
if(resFnd == RFCS_NONE)
{
//No context
_tprintf(L"ERROR: (0x%X) CertOpenStore(no_context) failed\n", ::GetLastError());
}
else
{
//Error
_tprintf(L"ERROR: (0x%X) CertOpenStore(%i) data failed\n", ::GetLastError(), i);
}
break;
}
}
if(hStore)
{
::CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
hStore = NULL;
}
}
void PrintDualSignatureInformation(PCMSG_SIGNER_INFO pSignerInfo)
{
//Loop through unauthenticated attributes
for(DWORD a = 0; a < pSignerInfo->UnauthAttrs.cAttr; a++)
{
#ifndef szOID_NESTED_SIGNATURE
#define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
#endif
//We need szOID_NESTED_SIGNATURE att
if(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId &&
lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId, szOID_NESTED_SIGNATURE) == 0)
{
HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, NULL, NULL, NULL);
if(hMsg)
{
if(::CryptMsgUpdate(hMsg,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData,
TRUE))
{
DWORD dwSignerInfo = 0;
::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo);
if(dwSignerInfo != 0)
{
PCMSG_SIGNER_INFO pSignerInfo2 = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwSignerInfo];
if(pSignerInfo2)
{
if(::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM,
0, (PVOID)pSignerInfo2, &dwSignerInfo))
{
CRYPT_DATA_BLOB c7Data;
c7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData;
c7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData;
//Try to locate the appropriate store & print from it
FindAppropriateStoreAndPrintCertificateInformation(pSignerInfo2, &c7Data, L"Dual Signer Certificate", FALSE);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pSignerInfo2;
pSignerInfo2 = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(PCMSG_SIGNER_INFO) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgUpdate(dual-sig) failed\n", ::GetLastError());
//Close message
::CryptMsgClose(hMsg);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) failed\n", ::GetLastError());
}
}
}