Getting a Specific Server (CA?) Ceritificate (most recently installed, specifically) from CA Store

Osman Zakir 121 Reputation points
2020-10-26T23:00:33.007+00:00

I have a web server app I wrote in C++ that I had a Let's Encrypt certificate issued for. I was using it by copy-pasting the contents into a header file, but I want to try to loading the certificate from the server store so I installed it to the CA store. I'm hosting the app on my computer.

I'm using this code to get the Root certs from the Root store:

template<class = void>
void load_root_certificates(ssl::context &ctx, boost::system::error_code &ec)
{
PCCERT_CONTEXT pcert_context = nullptr;
const char *pzstore_name = "ROOT";

// Try to open root certificate store
// If it succeeds, it'll return a handle to the certificate store
// If it fails, it'll return NULL
auto hstore_handle = CertOpenSystemStoreA(NULL, pzstore_name);
char *data = nullptr;
std::string certificates;
X509 *x509 = nullptr;
BIO *bio = nullptr;
if (hstore_handle != nullptr)
{
// Extract the certificates from the store in a loop
while ((pcert_context = CertEnumCertificatesInStore(hstore_handle, pcert_context)) != NULL)
{
x509 = d2i_X509(nullptr, const_cast<const BYTE**>(&pcert_context->pbCertEncoded), pcert_context->cbCertEncoded);
bio = BIO_new(BIO_s_mem());
if (PEM_write_bio_X509(bio, x509)) 
{
auto len = BIO_get_mem_data(bio, &data);
if (certificates.size() == 0)
{
certificates = { data, static_cast<std::size_t>(len) };
ctx.add_certificate_authority(boost::asio::buffer(certificates.data(), certificates.size()), ec);
if (ec)
{
BIO_free(bio);
X509_free(x509);
CertCloseStore(hstore_handle, 0);
return;
}
}
else
{
certificates.append(data, static_cast<std::size_t>(len));
ctx.add_certificate_authority(boost::asio::buffer(certificates.data(), certificates.size()), ec);
if (ec)
{
BIO_free(bio);
X509_free(x509);
CertCloseStore(hstore_handle, 0);
return;
}
}
}
BIO_free(bio);
X509_free(x509);
}
CertCloseStore(hstore_handle, 0);
}
const std::string certs{ certificates };
}

I can use this same code, except with "CA" as the store name, to get server certs, right? But how do I only get the one I just installed, instead of getting all of them? Is there a way to do this?

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,613 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,722 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Rita Han - MSFT 2,161 Reputation points
    2020-10-27T06:34:19.203+00:00

    Hello @Osman Zakir ,

    I can use this same code, except with "CA" as the store name

    Your code works for me to open the "CA" store. You can use CertEnumSystemStore to list the names of existing system stores to see if there is a "CA" store in your system.

    But how do I only get the one I just installed, instead of getting all of them? Is there a way to do this?

    You can find a specific certificate via CertFindCertificateInStore through specifying a dwFindType like CERT_FIND_EXISTING etc. In the certificate store, there seems no installed time property for certificates.

    Update: Show an example.

    1. Use CERT_FIND_ISSUER_STR to find certificates have the target issuer name.
    2. Check if the found certificate (pDesiredCert->pCertInfo) has the same SerialNumber, NotBefore etc. with the target ones. If yes, you found the specific certificate successfully.

    code sample:

    // Use CERT_FIND_ISSUER_STR to find certificates has the target issuer name.  
    if (pDesiredCert = CertFindCertificateInStore(  
    	hSystemStore,  
    	X509_ASN_ENCODING,             
    	0,                            
    	CERT_FIND_ISSUER_STR,      																	  
    	L"Target issuer name",									  
    	NULL))                     																											  
    {  
    	printf("The desired certificate was found. \n");  
    	// Check if the found certificate has the same SerialNumber, NotBefore etc. with the target ones. If yes, you found the specific certificate successfully.  
    }  
    else  
    {  
    	printf("Could not find the desired certificate. Error: %d\n", GetLastError());  
    }  
    

    Update 2: Decode the issuer name for comparing to check if it is the one we are finding. (Since some member of the CERT_CONTEXT are encoded, to get the raw data you need to decode at first.)

    //Decode issuer name for later comparing to see if it is the one we are finding.  
    DWORD requiredSize;  
    BOOL result = CryptDecodeObject(X509_ASN_ENCODING, X509_NAME, pDesiredCert->pCertInfo->Issuer.pbData, pDesiredCert->pCertInfo->Issuer.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &requiredSize);  
    DWORD err = GetLastError();  
    BYTE* decodedName;  
    if (!(decodedName = (BYTE*)malloc(requiredSize)))  
    {  
    	printf("Memory allocation failed.");  
    }  
    result = CryptDecodeObject(X509_ASN_ENCODING, X509_NAME, pDesiredCert->pCertInfo->Issuer.pbData, pDesiredCert->pCertInfo->Issuer.cbData, CRYPT_DECODE_NOCOPY_FLAG, decodedName, &requiredSize);  
    
    CERT_NAME_INFO *pDecodeName = (CERT_NAME_INFO*)decodedName;  
    printf("The cRDN is -> %d \n", pDecodeName->cRDN);  
    for (DWORD i = 0; i < pDecodeName->cRDN; i++)  
    {  
    	printf("The OID is -> ");  
    	printf("%s\n", pDecodeName->rgRDN[i].rgRDNAttr->pszObjId); szOID_COMMON_NAME;  
    	printf("The dwValueType is -> ");  
    	printf("%d\n", pDecodeName->rgRDN[i].rgRDNAttr->dwValueType);  
    	printf("The string is ->");  
    
    	if(CERT_RDN_PRINTABLE_STRING == pDecodeName->rgRDN[i].rgRDNAttr->dwValueType)  
    		printf("%s", pDecodeName->rgRDN[i].rgRDNAttr->Value.pbData);  
    	else  
    	    wprintf(L"%s", pDecodeName->rgRDN[i].rgRDNAttr->Value.pbData);  
    
    	// TODO: Compare the value you found with the target value, like common name etc.  
    
    	printf("\n");  
    }  
    

    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.