Programmatically getting the digital signature's digest algorithm for msi files in C++

2

I want to get the actual signature's algorithm (not the certificate's digest algorithm)­ that was used for signing a Windows installer file (msi).

Using Microsoft's crypt API, I was able to get what I want for exe's easily. By 1st getting the HCRYPTMSG, then getting the signer information size, then the signer information PCMSG_SIGNER_INFO and finally by getting the algorithm itself :

pSignerInfo->HashAlgorithm.pszObjId

BOOL bIsOk;
DWORD dwEncoding=0, dwContentType = 0, dwFormatType = 0, dwSignerInfo = 0;
std::string szFileName= "pathtothefile";

//Get message handle and store handle from the signed file.
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
bIsOk = CryptQueryObject(CERT_QUERY_OBJECT_FILE, szFileName.c_str(), CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL,
    0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL);

For some reason running my code on msi installer files always returns

CRYPT_E_NO_MATCH 0x80092009 Cannot find the requested object.

Of course I have verified the paths and all the possible flags to the CryptQueryObject function but I feel the error might be misleading.

I'm wondering if the crypt API isn't compatible with msi files to begin with.

All topics out there seem to relate to the certificate's digest algorithm only.

Thanks in advance

winapi
windows-installer
digital-signature
asked on Stack Overflow May 10, 2019 by PhantomLord • edited May 10, 2019 by PhantomLord

2 Answers

1

The answer to your problem is in the documentation for CryptQueryObject:

CERT_QUERY_OBJECT_FILE: This parameter is a pointer to a null-terminated Unicode string that contains the path and name of the file to query.

Unicode in Windows documentation means UTF-16LE. However, you are passing a std::string (using char as the code unit representation). A std::string (on Windows) is encoded using either ASCII or ANSI (codepage encoding). You're going to have to change that to a std::wstring instead, which uses wchar_t as its code unit representation:

std::wstring szFileName= L"pathtothefile";

wchar_t unambiguously means UTF-16LE on Windows.

answered on Stack Overflow May 10, 2019 by IInspectable
0

Unicode: You need to use a Unicode string for the call (file name).


CString: As a quick test I used a standard CString in Visual Studio 2017 (Unicode). Please check. Just to get it to run for further debugging. Maybe you can include <atlstr.h> if you don't include CString already.

//#include "atlstr.h"
BOOL bIsOk; DWORD dwEncoding = 0, dwContentType = 0, dwFormatType = 0, dwSignerInfo = 0;
CString test = "C:\\LibreOffice_6.2.3_Win_x64.msi";

<..>

bIsOk = CryptQueryObject(CERT_QUERY_OBJECT_FILE, test, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL,
    0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL);

Links:

answered on Stack Overflow May 10, 2019 by Stein Åsmul • edited May 10, 2019 by Stein Åsmul

User contributions licensed under CC BY-SA 3.0