sticky
HOWTO: Verify the digital signature of a file RRS feed

  • Question

  • Hello all,

    Well, I recently implemented digital signature verification into RootRepeal.  I found out that there's not any good examples of how to do this out on the Internet.  So, here's some sample code that shows how to properly verify the digital signature on a file.  This seems to work, based on what I've seen, but it's mostly a result of reversing, so I may have missed something.  It also hasn't been tested extensively on all system configurations, but it seems to work on Windows 2000+.

    BOOLEAN IsFileDigitallySigned(PWCHAR FilePath)
    {
        //Author: AD, 2009
        PVOID Context;
        HANDLE FileHandle;
        DWORD HashSize = 0;
        PBYTE Buffer;
        PVOID CatalogContext;
        CATALOG_INFO InfoStruct;
        WINTRUST_DATA WintrustStructure;
        WINTRUST_CATALOG_INFO WintrustCatalogStructure;
        WINTRUST_FILE_INFO WintrustFileStructure;
        PWCHAR MemberTag;
        BOOLEAN ReturnFlag = FALSE;
        ULONG ReturnVal;
        GUID ActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;

        //Zero our structures.
        memset(&InfoStruct, 0, sizeof(CATALOG_INFO));
        InfoStruct.cbStruct = sizeof(CATALOG_INFO);
        memset(&WintrustCatalogStructure, 0, sizeof(WINTRUST_CATALOG_INFO));
        WintrustCatalogStructure.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
        memset(&WintrustFileStructure, 0, sizeof(WINTRUST_FILE_INFO));
        WintrustFileStructure.cbStruct = sizeof(WINTRUST_FILE_INFO);

        //Get a context for signature verification.
        if( !CryptCATAdminAcquireContext(&Context, NULL, 0) )
        {
            return FALSE;
        }

        //Open file.
        FileHandle = CreateFileW(FilePath, GENERIC_READ, 7, NULL, OPEN_EXISTING, 0, NULL);
        if( INVALID_HANDLE_VALUE == FileHandle )
        {
            CryptCATAdminReleaseContext(Context, 0);
            return FALSE;
        }

        //Get the size we need for our hash.
        CryptCATAdminCalcHashFromFileHandle(FileHandle, &HashSize, NULL, 0);
        if( HashSize == 0 )
        {
            //0-sized has means error!
            CryptCATAdminReleaseContext(Context, 0);
            CloseHandle(FileHandle);
            return FALSE;
        }

        //Allocate memory.
        Buffer = (PBYTE)calloc(HashSize, 1);

        //Actually calculate the hash
        if( !CryptCATAdminCalcHashFromFileHandle(FileHandle, &HashSize, Buffer, 0) )
        {
            CryptCATAdminReleaseContext(Context, 0);
            free(Buffer);
            CloseHandle(FileHandle);
            return FALSE;
        }

        //Convert the hash to a string.
        MemberTag = (PWCHAR)calloc((HashSize * 2) + 1, sizeof(WCHAR));
        for( unsigned int i = 0; i < HashSize; i++ )
        {
            swprintf(&MemberTag[i * 2], L"%02X", Buffer[i ]);
        }

        //Get catalog for our context.
        CatalogContext = CryptCATAdminEnumCatalogFromHash(Context, Buffer, HashSize, 0, NULL);
        if ( CatalogContext )
        {
            //If we couldn't get information
            if ( !CryptCATCatalogInfoFromContext(CatalogContext, &InfoStruct, 0) )
            {
                //Release the context and set the context to null so it gets picked up below.
                CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0);
                CatalogContext = NULL;
            }
        }
           
        //If we have a valid context, we got our info. 
        //Otherwise, we attempt to verify the internal signature.
        if( !CatalogContext )
        {
            WintrustFileStructure.cbStruct = sizeof(WINTRUST_FILE_INFO);
            WintrustFileStructure.pcwszFilePath = FilePath;
            WintrustFileStructure.hFile = NULL;
            WintrustFileStructure.pgKnownSubject = NULL;

            WintrustStructure.cbStruct = sizeof(WINTRUST_DATA);
            WintrustStructure.dwUnionChoice = WTD_CHOICE_FILE;
            WintrustStructure.pFile = &WintrustFileStructure;
            WintrustStructure.dwUIChoice = WTD_UI_NONE;
            WintrustStructure.fdwRevocationChecks = WTD_REVOKE_NONE;
            WintrustStructure.dwStateAction = WTD_STATEACTION_IGNORE;
            WintrustStructure.dwProvFlags = WTD_SAFER_FLAG;
            WintrustStructure.hWVTStateData = NULL;
            WintrustStructure.pwszURLReference = NULL;
        } else
        {
            //If we get here, we have catalog info!  Verify it.
            WintrustStructure.cbStruct = sizeof(WINTRUST_DATA);
            WintrustStructure.pPolicyCallbackData = 0;
            WintrustStructure.pSIPClientData = 0;
            WintrustStructure.dwUIChoice = WTD_UI_NONE;
            WintrustStructure.fdwRevocationChecks = WTD_REVOKE_NONE;
            WintrustStructure.dwUnionChoice = WTD_CHOICE_CATALOG;
            WintrustStructure.pCatalog = &WintrustCatalogStructure;
            WintrustStructure.dwStateAction = WTD_STATEACTION_VERIFY;
            WintrustStructure.hWVTStateData = NULL;
            WintrustStructure.pwszURLReference = NULL;
            WintrustStructure.dwProvFlags = 0;
            WintrustStructure.dwUIContext = WTD_UICONTEXT_EXECUTE;

            //Fill in catalog info structure.
            WintrustCatalogStructure.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
            WintrustCatalogStructure.dwCatalogVersion = 0;
            WintrustCatalogStructure.pcwszCatalogFilePath = InfoStruct.wszCatalogFile;
            WintrustCatalogStructure.pcwszMemberTag = MemberTag;
            WintrustCatalogStructure.pcwszMemberFilePath = FilePath;
            WintrustCatalogStructure.hMemberFile = NULL;
        }

        //Call our verification function.
        ReturnVal = WinVerifyTrust(0, &ActionGuid, &WintrustStructure);

        //Check return.
        ReturnFlag = SUCCEEDED(ReturnVal);

        //Free context.
        if( CatalogContext )
            CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0);

        //If we successfully verified, we need to free.
        if( ReturnFlag )
        {
            WintrustStructure.dwStateAction = WTD_STATEACTION_CLOSE;
            WinVerifyTrust(0, &ActionGuid, &WintrustStructure);
        }

        //Free memory.
        free(MemberTag);
        free(Buffer);
        CloseHandle(FileHandle);
        CryptCATAdminReleaseContext(Context, 0);

        return ReturnFlag;
    }


    To make this work, you need to include the following headers:
    #include <Softpub.h>
    #include <wincrypt.h>
    #include <wintrust.h>
    #include <mscat.h>
    #include <wchar.h>


    You also need to link with wintrust.lib - you can do this with:
    #pragma comment(lib, "wintrust")


    If you have any problems with this code, or if I've missed something, please let me know!  As usual, please use at your own risk, etc.

    Thanks,
    --AD

    EDIT: Spelling.
    Tuesday, June 9, 2009 3:09 PM

All replies