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);
}
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);
}