Search code examples
cwindowswinapicode-signing-certificateauthenticode

Windows API authenticode get root certificate


I want to walk the certificate chain of a authenticode signed PE binary using the Windows API.

To get the certificate store I followed the example from Microsoft:
https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
With that I get the leaf certificate and the intermediate certificate, but not the root certificate. Tested with different Windows binaries (eg. explorer.exe)
I tried the following loops to walk the store:

while (pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext));
while (pCertContext = CertEnumCertificatesInStore(hStore, pCertContext));

Is the root certificate not included in the authenticode signature?
Do I miss some option?


Solution

  • Thanks @RbMm for your suggestion with CertGetCertificateChain, that does solve my question.
    To get the whole chain, you need to start at the leaf certificate (store seams to start top-down).

    Adapted from https://learn.microsoft.com/de-de/windows/desktop/SecCrypto/example-c-program-creating-a-certificate-chain:

    CERT_INFO CertInfo;
    
    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;
    }
    
    CERT_ENHKEY_USAGE        EnhkeyUsage;
    CERT_USAGE_MATCH         CertUsage;
    CERT_CHAIN_PARA          ChainPara;
    
    EnhkeyUsage.cUsageIdentifier = 0;
    EnhkeyUsage.rgpszUsageIdentifier = NULL;
    CertUsage.dwType = USAGE_MATCH_TYPE_AND;
    CertUsage.Usage = EnhkeyUsage;
    ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
    ChainPara.RequestedUsage = CertUsage;
    
    if (!CertGetCertificateChain(
        NULL,                  // use the default chain engine
        pCertContext,          // pointer to the end certificate
        NULL,                  // use the default time
        NULL,                  // search no additional stores
        &ChainPara,            // use AND logic and enhanced key usage 
                               //  as indicated in the ChainPara 
                               //  data structure
        dwFlags,
        NULL,                  // currently reserved
        &pChainContext)) {
        cerr << "Error on CertGetCertificateChain" << endl;
        __leave;
    }
    
    PCERT_SIMPLE_CHAIN    rgpChain   = NULL;
    PCERT_CHAIN_ELEMENT   rgpElement = NULL;
    
    rgpChain = pChainContext->rgpChain[0];
    
    for (int j = 0; j < rgpChain->cElement; j++) {
        rgpElement = rgpChain->rgpElement[j];
        PrintCertificateInfo(rgpElement->pCertContext);
        cout << endl;
    }