Verifying If a File has Been Signed By a Specific Certificate in Device Cert Store

Is there a quick way to decide if a file has been signed by a specific certificate?  This is definitely part of Windows Mobile code, but it seems there is no easy-to-use APIs for this task. I spent some time investigating this using Windows Mobile AKU and the SDK. Here is my way of doing this:
 
1. Use CertFindCertificateInStore(seen in wincrypt.h in SDK) to find the certificate you want to use to verify a file; This function returns a PCCERT_CONTEXT.

2. Use MinCryptVerifySignedFileEx(seen in mincrypt.h in AKU) to verify a file is indeed signed by that certificate; The way this API verifies the file is trying to root chain the certficiate used to sign the file, following the certificate path to look for a root certficiate in the specified store.

Let's say we want to check if file my.dll is signed by a self-issued certificate called mycert, which is in the device's privileged certificate store. Now, we open the cert store, find our that certificate using the cert’s SHA-1 hash (or subject, etc), and use it to call MinCryptVerifySignedFileEx() to verify. If my.dll is signed by mycert, or is signed by a code signing certificate issued by the issuer of mycert, then the API return TRUE, meaning that my.dll is indeed signed and should be 'trusted'.

The core part of this who thing is this:

    DWORD CheckBinary(LPWSTR lpwsFileName, PCCERT_CONTEXT pDesiredCert)
    {
        DWORD dwRet;
        MINCRYPT_TRUST_INFO _trust = {0};   // Used by MinCryptVerifySignedFileEx, details see mincrypt.h
        MINCRYPT_ROOT_CERT _rootcert;       // Used by MinCryptVerifySignedFileEx
        MINCRYPT_TRUST_STATUS _trust_status = {0};

        _trust.version = MINCRYPT_TRUST_INFO_VER;
        _trust.flags   = 0;
        _trust.cRootCerts = 1;              // number of root certs in pRootCerts array
        _trust.cRevocationList = 0;         // number of entries in revocation list
        _trust.pRootCerts = &_rootcert;

        BYTE* pPubKey = pDesiredCert->pbCertEncoded;
        PCRYPT_DER_BLOB prgCertBlob = _rootcert.rgCertBlob;
        dwRet = MinAsn1ParseCertificate(pPubKey, pDesiredCert->cbCertEncoded, prgCertBlob);
        if(dwRet < 0)
        {
            wprintf(L"Error parsing the certificate!\n");
            return -1;
        }
        dwRet = MinCryptVerifySignedFileEx(
            MINCRYPT_FILE_NAME,
            lpwsFileName,
            &_trust,
            &_trust_status,
            0,
            0,
            0,
            0
            );
        if(dwRet != ERROR_SUCCESS)
        {
            //Error code can be found here: https://blogs.msdn.com/eldar/archive/2007/04/03/a-lot-of-hresult-codes.aspx
            //Or use VS error lookup tool
            //Note, if error is 6, then file not found.
            wprintf(L"The file %s was not certified by our cert!: Error: %x\n", lpwsFileName, dwRet);
        }
        else
        {
            wprintf(L"The file %s was certified by our cert!\n", lpwsFileName);
        }
        return dwRet;
    }

Note: In visual studio, you need to add paths to those headers and libs (crypto32.lib, mincrypt.lib, and rsaenh.lib are all in AKU common OAK lib directory).

Comments