How to retrieve information from multiple/dual code signatures on an executable file

匿名 (未验证) 提交于 2019-12-03 08:46:08

问题:

I've been using the following code (taken from KB323809 article) to retrieve information about the code signature on the executable file. This works fine for a single digital signature.

But how to retrieve information for multiple code signatures?

In that case the Microsoft code below simply retrives info only for the first signature.

My thought was to call CryptMsgGetParam with CMSG_SIGNER_COUNT_PARAM to get the number of signatures and then pass each signature index to the subsequent call to CryptMsgGetParam with CMSG_SIGNER_INFO_PARAM (in the code below.) But this approach always returns 1 signature, even if I clearly have more, like 3 in this example:

#include <windows.h> #include <wincrypt.h> #include <wintrust.h> #include <stdio.h> #include <tchar.h>  #pragma comment(lib, "crypt32.lib")  #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)  typedef struct {     LPWSTR lpszProgramName;     LPWSTR lpszPublisherLink;     LPWSTR lpszMoreInfoLink; } SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;  BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,                              PSPROG_PUBLISHERINFO Info); BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st); BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext); BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,                             PCMSG_SIGNER_INFO *pCounterSignerInfo);  int _tmain(int argc, TCHAR *argv[]) {     WCHAR szFileName[MAX_PATH];      HCERTSTORE hStore = NULL;     HCRYPTMSG hMsg = NULL;      PCCERT_CONTEXT pCertContext = NULL;     BOOL fResult;        DWORD dwEncoding, dwContentType, dwFormatType;     PCMSG_SIGNER_INFO pSignerInfo = NULL;     PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;     DWORD dwSignerInfo;     CERT_INFO CertInfo;          SPROG_PUBLISHERINFO ProgPubInfo;     SYSTEMTIME st;      ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));     __try     {         if (argc != 2)         {             _tprintf(_T("Usage: SignedFileInfo <filename>\n"));             return 0;         }  #ifdef UNICODE         lstrcpynW(szFileName, argv[1], MAX_PATH); #else         if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)         {             printf("Unable to convert to unicode.\n");             __leave;         } #endif          // Get message handle and store handle from the signed file.         fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,                                    szFileName,                                    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,                                    CERT_QUERY_FORMAT_FLAG_BINARY,                                    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"));          // 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(_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     {                        // Clean up.         if (ProgPubInfo.lpszProgramName != NULL)             LocalFree(ProgPubInfo.lpszProgramName);         if (ProgPubInfo.lpszPublisherLink != NULL)             LocalFree(ProgPubInfo.lpszPublisherLink);         if (ProgPubInfo.lpszMoreInfoLink != NULL)             LocalFree(ProgPubInfo.lpszMoreInfoLink);          if (pSignerInfo != NULL) LocalFree(pSignerInfo);         if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);         if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);         if (hStore != NULL) CertCloseStore(hStore, 0);         if (hMsg != NULL) CryptMsgClose(hMsg);     }     return 0; }  BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext) {     BOOL fReturn = FALSE;     LPTSTR szName = NULL;     DWORD dwData;      __try     {         // Print Serial Number.         _tprintf(_T("Serial Number: "));         dwData = pCertContext->pCertInfo->SerialNumber.cbData;         for (DWORD n = 0; n < dwData; n++)         {             _tprintf(_T("%02x "),               pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);         }         _tprintf(_T("\n"));          // Get Issuer name size.         if (!(dwData = CertGetNameString(pCertContext,                                           CERT_NAME_SIMPLE_DISPLAY_TYPE,                                          CERT_NAME_ISSUER_FLAG,                                          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; } 

回答1:

In addition to the answer of Daniel Sie.

Found an attribute (szOID_NESTED_SIGNATURE) that would contain the CMSG_SIGNER_INFO that need to be decoded with following steps (this is Delphi code but sense is clear):

  LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);   CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);   CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, @LSize);   CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, @LSize); 

The acquired CMSG_SIGNER_INFO (LNestedSignerInfo) is the nested signature (in our case SHA2 signature).

To obtain the time-stamp information (RFC3161) of that signature - search the Unauthenticated attribute with pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).

Found attribute would contain the CMSG_SIGNER_INFO of the time-stamp counter signature, that also need to be decoded by previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).

The CERT_CONTEXT of nested signature or timestamp counter signature is need to be searched in store with is obtained from corresponding HCRYPTMSG (result of CryptMsgOpenToDecode).

  LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);   LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg); 

Example to decode szOID_RFC3161_counterSign attribute:

LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr; for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do begin   if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then   begin     LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);     if not Assigned(LNestedTimeStampMsg) then       RaiseLastOSError;     try       if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then         RaiseLastOSError;        if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, @LSize) then         RaiseLastOSError;       GetMem(LTimeStampSigner, LSize);       try         if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, @LSize) then           RaiseLastOSError;          LAttr := LTimeStampSigner.AuthAttrs.rgAttr;         for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do         begin           if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then           begin             LSize := SizeOf(LFileTime);             if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,               szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, @LFileTime, @LSize) then               RaiseLastOSError;              if FileTimeToLocalFileTime(@LFileTime, LLocalFileTime)               and FileTimeToSystemTime(@LLocalFileTime, LSystemTime) then                 SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)             else               SHA2TimeStamp := 0;            end;           Inc(LAttr);         end;        finally         FreeMem(LTimeStampSigner);       end;     finally       if not CryptMsgClose(LNestedTimeStampMsg) then         RaiseLastOSError;     end;   end;    Inc(LNestedSignerAttr); end; 


回答2:

Authenticode stores secondary signatures in the UnauthenticatedAttributes of primary signer (index 0), instead of additional PKCS 7 signer.

From the primary signature, search the UnauthenticatedAttribue for below:

//Indicates the attribute is an octet encoded PKCS7

define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"

The encoded object of this attribute is a full PKCS 7 signer.

Thanks.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!