Skip to content
⌘ NSIS Forum Archive

Test for a Valid Certificate?

12 posts

nsnb#

Test for a Valid Certificate?

Is there a way (or plugin) to test a digitally signed installer (with a code signing certificate), from within that installer, that the digital signature exists and valid?

That is, the the file has not been tampered with after it was signed?

I know how to do it visually, by looking at the corresponding Properties tab. Is it possible to do this programmatically from within the installer?

Thanks.
MSG#
Wouldn't this be a chicken-egg problem? You can't enter the checksum in the installer without changing the installer's checksum...

Though I do seem to recall some signing solutions existing. Maybe someone else knows.
nsnb#
Originally posted by MSG
Wouldn't this be a chicken-egg problem? You can't enter the checksum in the installer without changing the installer's checksum...
Not really a chicken-and-egg because I don't expect to read the installer's checksum for that: The digital certificate is appended and itself includes a stronger hashing of the entire file.

The nice thing about those digital certificates is that if anyone modifies anything inside the file (e.g. infecting with a virus), one could see immediately from the tab that it is no longer signed by so called "verified publisher".

So, all we really need is a way to look at this "tab" and tell go/no-go.

Any idea how to do that?
820815#
Originally posted by MSG
You can't enter the checksum in the installer without changing the installer's checksum...
There's not a problem to fix its last four bytes.
Anders#
There is really no point to do this, NSIS already checks itself with a CRC (And "pirates" will get around it anyways, or just extract the files without running the installer)

If you really want to do this, call Wintrust::CryptCATAdminCalcHashFromFileHandle (and WTHelperGetProvSignerFromChain or CryptCATAdminEnumCatalogFromHash?) and WinVerifyTrust with the system plugin

See also:


systracer#
Anders, sorry for raising old topic, but how it can be done? I'll try this:

FileOpen $R0 "C:\Test.exe" r
System::Call 'Kernel32::LoadLibrary(t "wintrust.dll")i.r0'
System::Call 'Kernel32::GetProcAddress(i r0, m "CryptCATAdminCalcHashFromFileHandle")i.r1'
System::Call "::$1(i R0, i 0, *i r2, i 0)?e"
Pop $0
FileClose $R0

But $2 has no hash. Please help with this, if you can...
Anders#
You don't have to manually call LoadLibrary.

The parameters should look like (i,*i,i,i0). First call it as (i $handle,*i.r2,i0,i0) and $2 will contain the size. Then allocate and call again with the third parameter set to the address of the memory you allocated.

If you are using NSIS3 you can try calling it once with something like System::Call 'wintrust::CryptCATAdminCalcHashFromFileHandle(p $handle,*i 999, @r3, i0)'
systracer#
Good day! Can't figure out the CryptCATAdminEnumCatalogFromHash function. If you can, please tell me what my mistake is. I simplified the code, there is no error checking.

FileOpen $0 "C:\test.exe" r
System::Call 'wintrust::CryptCATAdminCalcHashFromFileHandle(p r0, *i 20, @r1, i0)'
FileClose $0
;$1 has hash
System::Call 'wintrust::CryptCATAdminAcquireContext(*i r0, i0, i0)'
System::Call 'wintrust::CryptCATAdminEnumCatalogFromHash(i r0, *p r1, i 20, i0, i0)i.r2'
System::Call 'wintrust::CryptCATAdminReleaseContext(i r0)'

$2 is always 0, i.e. not return handle to the catalog context. Thank you.
systracer#
Maybe this helps, or this code:
    bool CheckByCat(const wchar_t *pwFileName)
{
HANDLE hCat;
if (!CryptCATAdminAcquireContext(&hCat, NULL, 0))
return false;

bool out = false;


HANDLE f = CreateFile(
pwFileName, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);


if (f != INVALID_HANDLE_VALUE)
{
BYTE hash[1024];
DWORD cb = sizeof(hash);
if (CryptCATAdminCalcHashFromFileHandle(f, &cb, hash, 0))
{
if(CryptCATAdminEnumCatalogFromHash(hCat, hash, cb, 0, 0))
out = true;
}
}

CryptCATAdminReleaseContext(hCat, 0);
::CloseHandle(f);

return out;
}

bool CheckByWinVerifyTrust(const wchar_t *pwFileName)
{
WINTRUST_FILE_INFO wfi={sizeof(WINTRUST_FILE_INFO), 0};
wfi.pcwszFilePath = pwFileName;
wfi.hFile = NULL;

WINTRUST_DATA wtd={sizeof(WINTRUST_DATA),0};
wtd.dwUIChoice = WTD_UI_NONE;
wtd.fdwRevocationChecks = WTD_REVOKE_NONE;
wtd.dwUnionChoice = WTD_CHOICE_FILE;
wtd.pFile = &wfi;

GUID g = WINTRUST_ACTION_GENERIC_VERIFY_V2;
LONG r = WinVerifyTrust(NULL, &g, &wtd);
return (r==ERROR_SUCCESS);
}
Anders#
!include LogicLib.nsh
!define /ifndef MAX_PATH 260
Section 
FileOpen $0 "$windir\explorer.exe" r
System::Call 'wintrust::CryptCATAdminCalcHashFromFileHandle(p r0, *i 666 r3, @r1, i0)i.r2'
FileClose $0
DetailPrint "succ=$2 size=$3"
System::Call 'wintrust::CryptCATAdminAcquireContext(*p 0 r0, p0, i0)i.r2'
${If} $2 <> 0
    System::Call 'wintrust::CryptCATAdminEnumCatalogFromHash(p r0, p r1, i r3, i0, p0)p.r2 ?e'
    Pop $4
    ${If} $2 P<> 0
        DetailPrint "Catalog context handle: $2"
        System::Call '*$1(&l4,&w${MAX_PATH})' ; Note: Overwriting the hash data here, allocate new memory here if you still need the hash data
        System::Call 'wintrust::CryptCATCatalogInfoFromContext(pr2, pr1, i0)i.r4'
        ${If} $4 <> 0
            System::Call '*$1(&l4,&w${MAX_PATH}.r5)'
            DetailPrint "Catalog path: $5"
        ${EndIf}
        System::Call 'wintrust::CryptCATAdminReleaseCatalogContext(pr0, pr2, i0)'
    ${Else}
        DetailPrint "wintrust::CryptCATAdminEnumCatalogFromHash failed with error $4"
    ${EndIf}
    System::Call 'wintrust::CryptCATAdminReleaseContext(p r0, i0)'
${EndIf}
SectionEnd 
If there is a catalog you can call WinVerifyTrust with WTD_CHOICE_CATALOG and WINTRUST_CATALOG_INFO data.