I've been struggling for awhile to amend this code sample from Microsoft that shows (somewhat outdated) way how to retrieve a code signature information from an executable file. It works but it doesn't retrieve information if a binary file is dual-signed.
So I did some research and tried to rewrite it to make it recognize dual-signatures that are present in many modern executables in Windows. Unfortunately there are very few (nebulous) suggestion available (1), (2), such as those to use UnauthenticatedAttributes
and szOID_NESTED_SIGNATURE
(whatever that means) but only to retrieve a time stamp.
So I attempted to rewrite that Microsoft code, and here's what I got:
(To build it simply copy it into Visual Studio console project & change the .exe file path. The code that deals with dual-signatures is in the PrintDualSignatureInfo()
function.)
#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;
}
Unfortunately what I came up with doesn't always work correctly when dealing with dual signatures.
For instance, a working example. If I run it on Sysiternal's ProcMon, that has a dual-signature as we can see from Windows Explorer:
My code correctly retrieves both SHA1
and SHA256
signatures:
But, here's a non-working example. If I run it on some other dual-signed file, say Putty executable, which also has a dual signature:
The code above retrieves the same SHA256
cert twice:
Any idea why?
PS. This doesn't just happen with the Putty's signature. There are other dual-signed executables that exhibit the same behavior.
Check the signature on an EXE or MSI file Right-click the EXE or MSI file and select Properties. Click the Digital Signatures tab to check the signature.
When a . dll and/or .exe file is digitally signed by a signer, you can confirm the same from the said file's properties. To detect whether the assembly file is signed or not, right click on the file and click the 'Properties' from the context menu.
Steps to Sign Executable Files Insert the USB token that you got from your CA into your system. Open the software SafeNet Client that you installed in your system. Open the command-line tool SignTool. Use the following command to digitally sign and timestamp your executable using SHA-256.
I think I got it to work:
Here's the updated code. Please make sure to check before you use it and post a comment if there are any issues:
#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());
}
}
}
The reason the digest algorithm in your output does not match the digest algorithm shown in file properties is because what you are showing in your output is the digest algorithm of the first certificate in the certificate chain for the signature, not the digest algorithm of the Authenticode signature itself. It's like this:
+-----------+ +-------------------+ +---------+ | Root Cert | signs | Intermediate Cert | signs | PE Data | |-----------|=========>|-------------------|=========>|---------| | SHA256 | | SHA256 | | SHA1 | +-----------+ +-------------------+ +---------+ ^ ^ | | You are showing But you this want to show this
The way Authenticode works is that first the file digest is computed using what is sometimes referred to (by Microsoft) as the "digest algorithm." That digest is then signed with a signing key whose certificate is provided. But that certificate itself is signed by computing its digest with what's referred to as "signature digest" and signing it with a key from a certificate higher up in the certificate chain and so on.
The first certificate in the certificate chain can be obtained with the CertFindCertificateInStore
function. You are then supposed to keep invoking CertFindCertificateInStore
in a while loop to get the other certificates. What you do in your code is obtain the first certificate in the certificate chain (with CertFindCertificateInStore
) and print its signature digest algorithm. What you want to do instead is obtain and print the digest algorithm for the file signature. You can do that with CryptMsgGetParam
with the CMSG_SIGNER_INFO_PARAM
flag, and you do obtain it, you just don't print it.
Another way to think about this is in terms of the relationship between MSG_SIGNER_INFO
and CERT_INFO
. It's not a 1-1 relationship where the information needs to match. It's more like:
+---------------+ +---------------+ | SIGNER_INFO 1 | | SIGNER_INFO 2 | |---------------| |---------------| | SHA1 | | SHA256 | +---------------+ +---------------+ | | | +-------------+ | +-------------+ +---| CERT_INFO 1 | +---| CERT_INFO 3 | |-------------| |-------------| | SHA256 | | SHA256 | +-------------+ +-------------+ | | +-------------+ +-------------+ | CERT_INFO 2 | | CERT_INFO 4 | |-------------| |-------------| | SHA1 | | SHA1 | +-------------+ +-------------+
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With