Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Amended code to retrieve dual signature information from PE executable in Windows?

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:

enter image description here

My code correctly retrieves both SHA1 and SHA256 signatures:

enter image description here

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:

enter image description here

The code above retrieves the same SHA256 cert twice:

enter image description here

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.

like image 207
MikeF Avatar asked Jun 21 '18 19:06

MikeF


People also ask

How do I view an executable digital signature?

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.

How can I tell if a signature is DLL?

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.

How do I create a signed exe file?

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.


2 Answers

I think I got it to work:

enter image description here

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());
        }
    }
}
like image 38
MikeF Avatar answered Oct 21 '22 08:10

MikeF


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     |
            +-------------+                    +-------------+
like image 60
mnistic Avatar answered Oct 21 '22 06:10

mnistic