修改过的代码可以从Windows中的PE可执行文件中检索双重签名信息?

时间:2018-06-21 19:59:10

标签: c++ winapi digital-signature code-signing authenticode

我一直在努力修改Microsoft的this code sample,以显示(有些过时)如何从可执行文件中检索代码签名信息的方式。它可以工作,但是如果二进制文件是双重签名的,则不会检索信息。

因此,我做了一些研究,并试图对其进行重写,以使其能够识别Windows中许多现代可执行文件中存在的双重签名。不幸的是,几乎没有(模糊的)建议可用(1)(2),例如那些使用UnauthenticatedAttributesszOID_NESTED_SIGNATURE的建议(无论如何),但仅用于检索时间戳。

所以我试图重写该Microsoft代码,这就是我得到的:

(要构建它,只需将其复制到Visual Studio控制台项目中并更改.exe文件路径。用于处理双重签名的代码在PrintDualSignatureInfo()函数中。) < / p>

#include "stdafx.h"

//SOURCE:
//      https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
//

#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#include <atlconv.h>

#pragma comment(lib, "crypt32.lib")

#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)

typedef struct {
    LPWSTR lpszProgramName;
    LPWSTR lpszPublisherLink;
    LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;



BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
                             PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
                            PCMSG_SIGNER_INFO *pCounterSignerInfo);
void PrintSignatureAlgorithm(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo);
void PrintDualSignatureInfo(PCMSG_SIGNER_INFO pSignerInfo);
void PrintCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertName);


int main()
{
     WCHAR szFileName[MAX_PATH]; 
    HCERTSTORE hStore = NULL;
    HCRYPTMSG hMsg = NULL; 
    BOOL fResult;   
    DWORD dwEncoding, dwContentType, dwFormatType;
    PCMSG_SIGNER_INFO pSignerInfo = NULL;
    DWORD dwSignerInfo;
    SPROG_PUBLISHERINFO ProgPubInfo;

    ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
    __try
    {
        LPCTSTR pExePath = L"C:\\Users\\UserName\\Downloads\\procmon.exe";  //works
        //pExePath = L"C:\\Users\\UserName\\Downloads\\putty.exe";      //doesnt work

        lstrcpynW(szFileName, pExePath, MAX_PATH);

        // Get message handle and store handle from the signed file.
        fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
                                   szFileName,
                                   CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
                                   CERT_QUERY_FORMAT_FLAG_BINARY,
                                   0,
                                   &dwEncoding,
                                   &dwContentType,
                                   &dwFormatType,
                                   &hStore,
                                   &hMsg,
                                   NULL);
        if (!fResult)
        {
            _tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
            __leave;
        }

        // Get signer information size.
        fResult = CryptMsgGetParam(hMsg, 
                                   CMSG_SIGNER_INFO_PARAM, 
                                   0, 
                                   NULL, 
                                   &dwSignerInfo);
        if (!fResult)
        {
            _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
            __leave;
        }

        // Allocate memory for signer information.
        pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
        if (!pSignerInfo)
        {
            _tprintf(_T("Unable to allocate memory for Signer Info.\n"));
            __leave;
        }

        // Get Signer Information.
        fResult = CryptMsgGetParam(hMsg, 
                                   CMSG_SIGNER_INFO_PARAM, 
                                   0, 
                                   (PVOID)pSignerInfo, 
                                   &dwSignerInfo);
        if (!fResult)
        {
            _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
            __leave;
        }

        // Get program name and publisher information from 
        // signer info structure.
        if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
        {
            if (ProgPubInfo.lpszProgramName != NULL)
            {
                wprintf(L"Program Name : %s\n",
                    ProgPubInfo.lpszProgramName);
            }

            if (ProgPubInfo.lpszPublisherLink != NULL)
            {
                wprintf(L"Publisher Link : %s\n",
                    ProgPubInfo.lpszPublisherLink);
            }

            if (ProgPubInfo.lpszMoreInfoLink != NULL)
            {
                wprintf(L"MoreInfo Link : %s\n",
                    ProgPubInfo.lpszMoreInfoLink);
            }
        }

        _tprintf(_T("\n"));

        // Print Signer certificate information.
        PrintCertificateInfo(hStore, pSignerInfo, L"Signer Certificate");


        //Look for dual signature
        PrintDualSignatureInfo(pSignerInfo);

    }
    __finally
    {               
        // Clean up.
        if (ProgPubInfo.lpszProgramName != NULL)
            LocalFree(ProgPubInfo.lpszProgramName);
        if (ProgPubInfo.lpszPublisherLink != NULL)
            LocalFree(ProgPubInfo.lpszPublisherLink);
        if (ProgPubInfo.lpszMoreInfoLink != NULL)
            LocalFree(ProgPubInfo.lpszMoreInfoLink);

        if (pSignerInfo != NULL) LocalFree(pSignerInfo);
        if (hStore != NULL) CertCloseStore(hStore, 0);
        if (hMsg != NULL) CryptMsgClose(hMsg);
    }
    return 0;
}



void PrintCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertName)
{

    if(hStore &&
        pSignerInfo)
    {
        PCCERT_CONTEXT pCertContext = NULL;
        CERT_INFO CertInfo = {0};
        PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
        SYSTEMTIME st;

        __try
        {
            // Search for the signer certificate in the temporary 
            // certificate store.
            CertInfo.Issuer = pSignerInfo->Issuer;
            CertInfo.SerialNumber = pSignerInfo->SerialNumber;

            pCertContext = CertFindCertificateInStore(hStore,
                                                      ENCODING,
                                                      0,
                                                      CERT_FIND_SUBJECT_CERT,
                                                      (PVOID)&CertInfo,
                                                      NULL);
            if (!pCertContext)
            {
                _tprintf(_T("CertFindCertificateInStore failed with %x\n"),
                    GetLastError());
                __leave;
            }

            // Print Signer certificate information.
            _tprintf(L"%s:\n\n", pStrCertName);     //(_T("Signer Certificate:\n\n"));        
            PrintCertificateInfo(pCertContext);
            _tprintf(_T("\n"));



            // Get the timestamp certificate signerinfo structure.
            if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
            {
                // Search for Timestamp certificate in the temporary
                // certificate store.
                CertInfo.Issuer = pCounterSignerInfo->Issuer;
                CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;

                pCertContext = CertFindCertificateInStore(hStore,
                                                    ENCODING,
                                                    0,
                                                    CERT_FIND_SUBJECT_CERT,
                                                    (PVOID)&CertInfo,
                                                    NULL);
                if (!pCertContext)
                {
                    _tprintf(_T("CertFindCertificateInStore failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Print timestamp certificate information.
                _tprintf(_T("TimeStamp Certificate:\n\n"));
                PrintCertificateInfo(pCertContext);
                _tprintf(_T("\n"));

                // Find Date of timestamp.
                if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
                {
                    _tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
                                                st.wMonth,
                                                st.wDay,
                                                st.wYear,
                                                st.wHour,
                                                st.wMinute);
                }
                _tprintf(_T("\n"));
            }

        }
        __finally
        {
            if (pCounterSignerInfo != NULL)
                LocalFree(pCounterSignerInfo);

            if (pCertContext != NULL)
                CertFreeCertificateContext(pCertContext);
        }
    }

}



void PrintDualSignatureInfo(PCMSG_SIGNER_INFO pSignerInfo)
{
    if(pSignerInfo)
    {

        for(DWORD a = 0; a < pSignerInfo->UnauthAttrs.cAttr; a++)
        {
            if(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId &&
                lstrcmpiA(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId, szOID_NESTED_SIGNATURE) == 0)
            {
                HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, NULL, NULL, NULL);
                if(hMsg)
                {
                    if(::CryptMsgUpdate(hMsg, 
                        pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData,
                        pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData,
                        TRUE))
                    {
                        DWORD dwSignerInfo = 0;
                        ::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo);
                        if(dwSignerInfo != 0)
                        {
                            PCMSG_SIGNER_INFO pSignerInfo2 = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwSignerInfo];
                            if(pSignerInfo2)
                            {
                                if(::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 
                                    0, (PVOID)pSignerInfo2, &dwSignerInfo))
                                {
                                    CRYPT_DATA_BLOB p7Data;
                                    p7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData;
                                    p7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData;

                                    HCERTSTORE hStore = ::CertOpenStore(CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, &p7Data);
                                    if(hStore)
                                    {
                                        // Print Signer certificate information.
                                        PrintCertificateInfo(hStore, pSignerInfo2, L"Dual Signer Certificate");

                                        //Close
                                        ::CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
                                    }
                                }

                                //Free mem
                                delete[] pSignerInfo2;
                                pSignerInfo2 = NULL;
                            }
                        }
                    }

                    //Close message
                    ::CryptMsgClose(hMsg);
                }

            }
        }


    }

}


void PrintSignatureAlgorithm(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo)
{
    if(pSigAlgo &&
        pSigAlgo->pszObjId)
    {
        PCCRYPT_OID_INFO pCOI = ::CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSigAlgo->pszObjId, 0);
        if(pCOI &&
            pCOI->pwszName)
        {
            _tprintf(L"%s", pCOI->pwszName);
        }
        else
        {
            USES_CONVERSION;
            _tprintf(L"%s",  A2W(pSigAlgo->pszObjId));
        }
    }
}


BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
    BOOL fReturn = FALSE;
    LPTSTR szName = NULL;
    DWORD dwData;

    __try
    {
        // Print Serial Number.
        _tprintf(_T("Serial Number: "));
        dwData = pCertContext->pCertInfo->SerialNumber.cbData;
        for (DWORD n = 0; n < dwData; n++)
        {
            _tprintf(_T("%02x "),
              pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
        }
        _tprintf(_T("\n"));


        //Hashing algoriths
        _tprintf(L"Signature Algorithm: ");
        PrintSignatureAlgorithm(&pCertContext->pCertInfo->SignatureAlgorithm);
        _tprintf(_T("\n"));


        // Get Issuer name size.
        if (!(dwData = CertGetNameString(pCertContext, 
                                         CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                         CERT_NAME_ISSUER_FLAG,
                                         NULL,
                                         NULL,
                                         0)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Allocate memory for Issuer name.
        szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
        if (!szName)
        {
            _tprintf(_T("Unable to allocate memory for issuer name.\n"));
            __leave;
        }

        // Get Issuer name.
        if (!(CertGetNameString(pCertContext, 
                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                CERT_NAME_ISSUER_FLAG,
                                NULL,
                                szName,
                                dwData)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // print Issuer name.
        _tprintf(_T("Issuer Name: %s\n"), szName);
        LocalFree(szName);
        szName = NULL;

        // Get Subject name size.
        if (!(dwData = CertGetNameString(pCertContext, 
                                         CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                         0,
                                         NULL,
                                         NULL,
                                         0)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Allocate memory for subject name.
        szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
        if (!szName)
        {
            _tprintf(_T("Unable to allocate memory for subject name.\n"));
            __leave;
        }

        // Get subject name.
        if (!(CertGetNameString(pCertContext, 
                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                0,
                                NULL,
                                szName,
                                dwData)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Print Subject Name.
        _tprintf(_T("Subject Name: %s\n"), szName);


        fReturn = TRUE;
    }
    __finally
    {
        if (szName != NULL) LocalFree(szName);
    }

    return fReturn;
}

LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
    LPWSTR outputString = NULL;

    outputString = (LPWSTR)LocalAlloc(LPTR,
        (wcslen(inputString) + 1) * sizeof(WCHAR));
    if (outputString != NULL)
    {
        lstrcpyW(outputString, inputString);
    }
    return outputString;
}

BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
                             PSPROG_PUBLISHERINFO Info)
{
    BOOL fReturn = FALSE;
    PSPC_SP_OPUS_INFO OpusInfo = NULL;  
    DWORD dwData;
    BOOL fResult;

    __try
    {
        // Loop through authenticated attributes and find
        // SPC_SP_OPUS_INFO_OBJID OID.
        for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
        {           
            if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, 
                        pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
            {
                // Get Size of SPC_SP_OPUS_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                            SPC_SP_OPUS_INFO_OBJID,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                            0,
                            NULL,
                            &dwData);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Allocate memory for SPC_SP_OPUS_INFO structure.
                OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
                if (!OpusInfo)
                {
                    _tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
                    __leave;
                }

                // Decode and get SPC_SP_OPUS_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                            SPC_SP_OPUS_INFO_OBJID,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                            0,
                            OpusInfo,
                            &dwData);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Fill in Program Name if present.
                if (OpusInfo->pwszProgramName)
                {
                    Info->lpszProgramName =
                        AllocateAndCopyWideString(OpusInfo->pwszProgramName);
                }
                else
                    Info->lpszProgramName = NULL;

                // Fill in Publisher Information if present.
                if (OpusInfo->pPublisherInfo)
                {

                    switch (OpusInfo->pPublisherInfo->dwLinkChoice)
                    {
                        case SPC_URL_LINK_CHOICE:
                            Info->lpszPublisherLink =
                                AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
                            break;

                        case SPC_FILE_LINK_CHOICE:
                            Info->lpszPublisherLink =
                                AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
                            break;

                        default:
                            Info->lpszPublisherLink = NULL;
                            break;
                    }
                }
                else
                {
                    Info->lpszPublisherLink = NULL;
                }

                // Fill in More Info if present.
                if (OpusInfo->pMoreInfo)
                {
                    switch (OpusInfo->pMoreInfo->dwLinkChoice)
                    {
                        case SPC_URL_LINK_CHOICE:
                            Info->lpszMoreInfoLink =
                                AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
                            break;

                        case SPC_FILE_LINK_CHOICE:
                            Info->lpszMoreInfoLink =
                                AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
                            break;

                        default:
                            Info->lpszMoreInfoLink = NULL;
                            break;
                    }
                }               
                else
                {
                    Info->lpszMoreInfoLink = NULL;
                }

                fReturn = TRUE;

                break; // Break from for loop.
            } // lstrcmp SPC_SP_OPUS_INFO_OBJID                 
        } // for 
    }
    __finally
    {
        if (OpusInfo != NULL) LocalFree(OpusInfo);      
    }

    return fReturn;
}

BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{   
    BOOL fResult;
    FILETIME lft, ft;   
    DWORD dwData;
    BOOL fReturn = FALSE;

    // Loop through authenticated attributes and find
    // szOID_RSA_signingTime OID.
    for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
    {           
        if (lstrcmpA(szOID_RSA_signingTime, 
                    pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
        {               
            // Decode and get FILETIME structure.
            dwData = sizeof(ft);
            fResult = CryptDecodeObject(ENCODING,
                        szOID_RSA_signingTime,
                        pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                        pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                        0,
                        (PVOID)&ft,
                        &dwData);
            if (!fResult)
            {
                _tprintf(_T("CryptDecodeObject failed with %x\n"),
                    GetLastError());
                break;
            }

            // Convert to local time.
            FileTimeToLocalFileTime(&ft, &lft);
            FileTimeToSystemTime(&lft, st);

            fReturn = TRUE;

            break; // Break from for loop.

        } //lstrcmp szOID_RSA_signingTime
    } // for 

    return fReturn;
}

BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{   
    PCCERT_CONTEXT pCertContext = NULL;
    BOOL fReturn = FALSE;
    BOOL fResult;       
    DWORD dwSize;   

    __try
    {
        *pCounterSignerInfo = NULL;

        // Loop through unathenticated attributes for
        // szOID_RSA_counterSign OID.
        for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
        {
            if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, 
                         szOID_RSA_counterSign) == 0)
            {
                // Get size of CMSG_SIGNER_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                           PKCS7_SIGNER_INFO,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
                           0,
                           NULL,
                           &dwSize);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Allocate memory for CMSG_SIGNER_INFO.
                *pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
                if (!*pCounterSignerInfo)
                {
                    _tprintf(_T("Unable to allocate memory for timestamp info.\n"));
                    __leave;
                }

                // Decode and get CMSG_SIGNER_INFO structure
                // for timestamp certificate.
                fResult = CryptDecodeObject(ENCODING,
                           PKCS7_SIGNER_INFO,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
                           0,
                           (PVOID)*pCounterSignerInfo,
                           &dwSize);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                fReturn = TRUE;

                break; // Break from for loop.
            }           
        }
    }
    __finally
    {
        // Clean up.
        if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
    }

    return fReturn;
}

不幸的是,在处理双重签名时,我的想法并不总是能正常工作。

例如,工作示例。如果我在Sysiternal的ProcMon上运行它,它将具有双重签名,正如我们在Windows资源管理器中看到的那样:

enter image description here

我的代码正确检索了SHA1SHA256签名:

enter image description here

但是,这是一个无效示例。如果我在其他双签名文件上运行它,请说Putty可执行文件,它也具有双签名:

enter image description here

上面的代码两次检索相同的SHA256证书:

enter image description here

知道为什么吗?

PS。这不仅发生在Putty的签名上。还有其他具有相同行为的双签名可执行文件。

2 个答案:

答案 0 :(得分:2)

输出中的摘要算法与文件属性中显示的摘要算法不匹配的原因是,您在输出中显示的是签名证书链中第一个证书的摘要算法,而不是摘要算法Authenticode签名本身。就像这样:

+-----------+          +-------------------+          +---------+
| Root Cert |  signs   | Intermediate Cert |  signs   | PE Data |
|-----------|=========>|-------------------|=========>|---------|
|  SHA256   |          |      SHA256       |          |  SHA1   |
+-----------+          +-------------------+          +---------+

                                ^                          ^
                                |                          |
                         You are showing                But you
                              this                    want to show
                                                          this

Authenticode的工作方式是,首先使用有时被Microsoft称为“摘要算法”的内容来计算文件摘要。然后使用提供其证书的签名密钥对该摘要进行签名。但是,该证书本身是通过使用所谓的“签名摘要”计算其摘要并使用来自证书链中较高级别的证书的密钥对其进行签名的,等等。

可以使用CertFindCertificateInStore函数获得证书链中的第一个证书。然后,您应该在while循环中继续调用CertFindCertificateInStore以获取其他证书。您在代码中所做的就是获取证书链中的第一个证书(使用CertFindCertificateInStore)并打印其签名摘要算法。相反,您要做的是获取并打印用于文件签名的摘要算法。您可以使用带有CryptMsgGetParam标志的CMSG_SIGNER_INFO_PARAM来做到这一点,并获得它,只是不打印它。


考虑这一点的另一种方式是根据MSG_SIGNER_INFOCERT_INFO之间的关系。这不是信息需要匹配的1-1关系。更像是:

+---------------+                  +---------------+
| SIGNER_INFO 1 |                  | SIGNER_INFO 2 |
|---------------|                  |---------------|
|     SHA1      |                  |    SHA256     |
+---------------+                  +---------------+
        |                                  |
        |   +-------------+                |   +-------------+
        +---| CERT_INFO 1 |                +---| CERT_INFO 3 |
            |-------------|                    |-------------|
            |   SHA256    |                    |   SHA256    |
            +-------------+                    +-------------+
                   |                                  |
            +-------------+                    +-------------+
            | CERT_INFO 2 |                    | CERT_INFO 4 |
            |-------------|                    |-------------|
            |    SHA1     |                    |    SHA1     |
            +-------------+                    +-------------+

答案 1 :(得分:1)

我想我可以使用它

enter image description here

这是更新的代码。请确保在使用前先检查一下,如果有任何问题,请发表评论:

#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());
        }
    }
}