winapicryptoapi

How to retrieve certs from windows certstore by serial number?


I am trying to retrieve a random certificate from the windows cert store, in this case a common Digicert root cert (this is only for testing). I modified some of the sample code I could find, but can't get it to work.

I am suspecting I may be encoding the search parameters wrong.The MSDN documentation is not very useful here, is issuer a SBCS or unicode string? Do I pass the issuer name, or the string as a fully qualified issuer (CN, OU, O, C, etc)? What else am I doing wrong?

The following is my fully self-contained test app, and therefore it's not "pretty", and doesn't have much error handling in place:

#pragma comment(lib, "crypt32.lib")

#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>

void panic(const char* s);
int main(void)
{
    #define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

    HCERTSTORE      hSystemStore;
    PCCERT_CONTEXT  pTargetCert = NULL;

    if (!(hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, 
        CERT_SYSTEM_STORE_LOCAL_MACHINE, L"Root")))
        panic("Error opening the Root store.");

    CERT_NAME_BLOB issuer;
    CRYPT_INTEGER_BLOB serial;

    const char* issuer_str = "DigiCert Global Root CA";
    const char* sn_string = "083be056904246b1a1756ac95991c74a";

    serial.cbData = (DWORD)strlen(sn_string) / 2;
    serial.pbData = new BYTE[serial.cbData];

    const char* pos = sn_string;
    for (size_t count = 0; count < serial.cbData; count++) {
        sscanf_s(pos, "%2hhx", &serial.pbData[count]);
        pos += 2;
    }

    issuer.cbData = (DWORD)strlen(issuer_str);
    issuer.pbData =  (BYTE *)issuer_str;

    CERT_ISSUER_SERIAL_NUMBER  findparams;
    findparams.Issuer = issuer;
    findparams.SerialNumber = serial;

    CERT_ID id;
    id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
    id.IssuerSerialNumber = findparams;

    if (!(pTargetCert = CertFindCertificateInStore(
        hSystemStore,           // Store handle.
        MY_ENCODING_TYPE,       // Encoding type.
        0,                      // Not used.
        CERT_FIND_CERT_ID,// Find type. Find a string in the 
        // certificate's subject.
        &id,          // The string to be searched for.
        pTargetCert)))           // Previous context.
        panic("Could not find the required certificate");

    if (pTargetCert)
        CertFreeCertificateContext(pTargetCert);
    if (hSystemStore)
    {
        if (!CertCloseStore(
            hSystemStore,
            CERT_CLOSE_STORE_CHECK_FLAG))
            panic("Could not close the certificate store");
    }
}

void panic(const char* s)
{
    fprintf(stderr, "%s: Error number %x.\n", s, GetLastError());
    exit(1);
} 

Solution

  • the CERT_NAME_BLOB Issuer must be encoded, but not point to plain text name. you can use here or CryptEncodeObjectEx with X509_NAME and filled CERT_NAME_INFO as input. or use CertStrToNameW

    and need use full name - "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA"

    then string "083be056904246b1a1756ac95991c74a" must be reversed ( it shown in certificate properties in reverse byte order, unlike say Thumbprint)

    the working code:

    void reverse(PBYTE pb, size_t cb )
    {
        if (cb)
        {
            PBYTE qb = pb + cb;
    
            do 
            {
                BYTE b = *--qb;
                *qb = *pb;
                *pb++ = b;
            } while (pb < qb);
        }
    }
    
    HRESULT FindCert(_Out_ PCCERT_CONTEXT* ppTargetCert, _In_ PCWSTR issuer_str, _In_ PCWSTR sn_string )
    {
        CERT_ID id = { CERT_ID_ISSUER_SERIAL_NUMBER }; 
    
        while (CryptStringToBinaryW(sn_string, 0, CRYPT_STRING_HEXRAW, 
            id.IssuerSerialNumber.SerialNumber.pbData, &id.IssuerSerialNumber.SerialNumber.cbData, 0, 0))
        {
            if (id.IssuerSerialNumber.SerialNumber.pbData)
            {
                reverse(id.IssuerSerialNumber.SerialNumber.pbData, id.IssuerSerialNumber.SerialNumber.cbData);
    
                while (CertStrToNameW(X509_ASN_ENCODING, issuer_str, CERT_X500_NAME_STR|CERT_NAME_STR_COMMA_FLAG, 0, 
                    id.IssuerSerialNumber.Issuer.pbData, &id.IssuerSerialNumber.Issuer.cbData, 0))
                {
                    if (id.IssuerSerialNumber.Issuer.pbData)
                    {
                        if (HCERTSTORE hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, 
                            CERT_SYSTEM_STORE_LOCAL_MACHINE|CERT_STORE_OPEN_EXISTING_FLAG|CERT_STORE_READONLY_FLAG, 
                            L"root"))
                        {
                            ULONG dwError = NOERROR;
    
                            if (PCCERT_CONTEXT pTargetCert = CertFindCertificateInStore(hSystemStore, 
                                X509_ASN_ENCODING, 0, CERT_FIND_CERT_ID, &id, 0))
                            {
                                *ppTargetCert = pTargetCert;
                            }
                            else
                            {
                                dwError = GetLastError();
                            }
    
                            CertCloseStore(hSystemStore, 0);
    
                            return dwError;
                        }
    
                        break;
                    }
    
                    if (0x400 < id.IssuerSerialNumber.Issuer.cbData)
                    {
                        return RPC_S_STRING_TOO_LONG;
                    }
    
                    id.IssuerSerialNumber.Issuer.pbData = (PBYTE)alloca(id.IssuerSerialNumber.Issuer.cbData);
                }
    
                break;
            }
    
            if (0x100 < id.IssuerSerialNumber.SerialNumber.cbData)
            {
                return RPC_S_STRING_TOO_LONG;
            }
    
            id.IssuerSerialNumber.SerialNumber.pbData = (PBYTE)alloca(id.IssuerSerialNumber.SerialNumber.cbData);
        }
    
        return GetLastError();
    }
    
        PCCERT_CONTEXT pTargetCert;
        if (NOERROR == FindCert(&pTargetCert, 
            L"C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA", 
            L"083be056904246b1a1756ac95991c74a"))
        {
            CertFreeCertificateContext(pTargetCert);
        }