locked
Extracting Certs from Server Cert Store? RRS feed

  • Question

  • Like how there's a way to extract certs from the root cert store, isn't there also a way to extract certs from the server cert store?  In my current situation I'm hard-coding the certificate into the ASIO SSL context for my app, but I want to know how to get them from the store in a loop.  I installed a new certificate just now and I want to use it, but I want to avoid having to hard-code it again if possible.

    Also, if I do this, will I still have to hard-code the RSA Private Key?  There doesn't seem to be a way to install it anywhere after all.

    I have this code for getting server certificate (this is server_certificate.hpp):

    #ifndef SERVER_CERTIFICATE_H
    #define SERVER_CERTIFICATE_H
    #include <boost/asio/buffer.hpp>
    #include <boost/asio/ssl/context.hpp>
    #include <cstddef>
    #include <memory>
    /*
     Load a signed certificate into the ssl context, and configure
     the context for use with a server.
    */
    inline void load_server_certificate(boost::asio::ssl::context &ctx)
    {
     /*
      This certificate was issued by Let's Encrypt using DNS record method of
      domain verification via the acme.sh client.
     */
     const std::string cert =
      "<insert cert blob text here>";
     const std::string key =
      "<insert key blob text here>";
     const std::string dh =
      "-----BEGIN DH PARAMETERS-----\n"
      "MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
      "+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
      "87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
      "YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
      "7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n"
      "ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==\n"
      "-----END DH PARAMETERS-----\n";
     ctx.set_password_callback(
      [](std::size_t, boost::asio::ssl::context::password_purpose) 
     {
      return "test";
     });
     ctx.set_options(boost::asio::ssl::context::default_workarounds | 
      boost::asio::ssl::context::no_sslv2 | 
      boost::asio::ssl::context::single_dh_use);
     ctx.use_certificate_chain(boost::asio::buffer(cert.data(), cert.size()));
     ctx.use_rsa_private_key(boost::asio::buffer(key.data(), key.size()), boost::asio::ssl::context::file_format::pem);
     ctx.use_tmp_dh(boost::asio::buffer(dh.data(), dh.size()));
    }
    #endif

    I put placeholder text there instead of the actual certificate chain (fullchain.cer) and private key, but in my actual code I have the actual certificate and key there.  Hard-coded. I just want to know if this can be automated by pulling the cert from a store (I installed the one I have, so it should be in a store, right?).  

    Here's root_certificate.hpp, for showing you the kind of automation I mean:

    #ifndef ROOT_CERTIFICATE_H
    #define ROOT_CERTIFICATE_H
    
    #include <boost/asio/ssl.hpp>
    #include <wincrypt.h>
    #include <iostream>
    #include <string>
    
    namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
    
    namespace detail
    {
    	// The template argument is gratuituous, to
    	// allow the implementation to be header-only.
    	//
    	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 };
    	}
    }
    
    // Load the certificate into an SSL context
    //
    // This function is inline so that its easy to take
    // the address and there's nothing weird like a
    // gratuituous template argument; thus it appears
    // like a "normal" function.
    //
    
    inline void load_root_certificates(ssl::context &ctx, boost::system::error_code &ec)
    {
    	detail::load_root_certificates(ctx, ec);
    }
    
    inline void load_root_certificates(ssl::context &ctx)
    {
    	boost::system::error_code ec;
    	detail::load_root_certificates(ctx, ec);
    	if (ec)
    	{
    		throw boost::system::system_error{ ec };
    	}
    }
    
    #endif

    Any help would be appreciated.  Thanks.

    Wednesday, June 19, 2019 11:27 PM

Answers

  • Like how there's a way to extract certs from the root cert store, isn't there also a way to extract certs from the server cert store?

    Perhaps you need to call CertOpenStore with dwFlags = CERT_STORE_PROV_SYSTEM_A | CERT_SYSTEM_STORE_LOCAL_MACHINE and pvPara = "MY".

    Also, if I do this, will I still have to hard-code the RSA Private Key? There doesn't seem to be a way to install it anywhere after all.

    It is surely possible to import a certificate and a private key from a PKCS#12 *.pfx file. The private key goes to a key container and the certificate in the certificate store gets a property that refers to the key container. Try running "certmgr.exe -r localMachine -s MY" (not certmgr.msc) in a Visual Studio Developer Command Prompt window to view these references. There is a separate access-control list on each key container.

    If the private key was marked as exportable when it was imported, then I imagine you can use CryptAcquireCertificatePrivateKey followed by either CryptExportKey or NCryptExportKey, to get a blob that you can then parse. However, Windows really wants you to use the key via its own crypto functions, rather than export it to OpenSSL or whatever. That way, your program can work even if the key container is implemented in tamperproof cryptoprocessor hardware that will never allow the key to be exported.

    • Marked as answer by DragonOsman2 Thursday, June 20, 2019 9:09 PM
    Thursday, June 20, 2019 3:57 PM

All replies

  • Like how there's a way to extract certs from the root cert store, isn't there also a way to extract certs from the server cert store?

    Perhaps you need to call CertOpenStore with dwFlags = CERT_STORE_PROV_SYSTEM_A | CERT_SYSTEM_STORE_LOCAL_MACHINE and pvPara = "MY".

    Also, if I do this, will I still have to hard-code the RSA Private Key? There doesn't seem to be a way to install it anywhere after all.

    It is surely possible to import a certificate and a private key from a PKCS#12 *.pfx file. The private key goes to a key container and the certificate in the certificate store gets a property that refers to the key container. Try running "certmgr.exe -r localMachine -s MY" (not certmgr.msc) in a Visual Studio Developer Command Prompt window to view these references. There is a separate access-control list on each key container.

    If the private key was marked as exportable when it was imported, then I imagine you can use CryptAcquireCertificatePrivateKey followed by either CryptExportKey or NCryptExportKey, to get a blob that you can then parse. However, Windows really wants you to use the key via its own crypto functions, rather than export it to OpenSSL or whatever. That way, your program can work even if the key container is implemented in tamperproof cryptoprocessor hardware that will never allow the key to be exported.

    • Marked as answer by DragonOsman2 Thursday, June 20, 2019 9:09 PM
    Thursday, June 20, 2019 3:57 PM
  • Thanks for the reply.  I think it's easier for me to do it the way it is now, so I think I'll keep it that way.  
    Thursday, June 20, 2019 9:10 PM