How to get information from several / two-code code signatures in an executable file

I used the following code (taken from KB323809 ) to get code signing information in an executable file. This works great for a single digital signature.

But how to get information for several code signatures?

In this case, the Microsoft code below simply returns information for the first signature only.

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 next call to CryptMsgGetParam using CMSG_SIGNER_INFO_PARAM (in the code below). But this approach always returns 1 signature, even if I have clearly more, for example 3 in this example:

enter image description here

 #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; } 
+6
source share
2 answers

In addition to Daniel C.'s answer.

The attribute ( szOID_NESTED_SIGNATURE ) was found that will contain CMSG_SIGNER_INFO , which must be decoded in the following steps (this is Delphi code, but the meaning 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 a sub-signature (in our case, the SHA2 signature).

To get the timestamp information (RFC3161) of this signature, search for the Unauthenticated attribute using pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).

The Found attribute would contain the CMSG_SIGNER_INFO signature of the time counter, which must also be decoded by the previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).

The CERT_CONTEXT of the nested signature of the signature counter or timestamp must be sought in the store obtained from the 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 for decoding the attribute szOID_RFC3161_counterSign:

 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; 
+4
source

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

From the initial signature, search the UnauthenticatedAttribue below:

// Indicates that the attribute is a PKCS7 encoded octet

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 subscriber.

Thanks.

+1
source

All Articles