46

Some working C++ code that I'm porting from Linux to Windows is failing on windows because SSL_get_verify_result() is returning X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY.

The code was using SSL_CTX_set_default_verify_paths() on Linux to tell SSL to just look in the standard default locations for the certificate store.

Is it possible to get OpenSSL to use the system certificate store?

2

6 Answers 6

55

I have done it earlier. Hope this helps, if this is exactly what you are looking for.

  1. Load your certificate (in PCCERT_CONTEXT structure) from Windows Cert store using Crypto APIs.
  2. Get encrypted content of it in binary format as it is. [PCCERT_CONTEXT->pbCertEncoded].
  3. Parse this binary buffer into X509 certificate Object using OpenSSL's d2i_X509() method.
  4. Get handle to OpenSSL's trust store using SSL_CTX_get_cert_store() method.
  5. Load above parsed X509 certificate into this trust store using X509_STORE_add_cert() method.
  6. You are done!
Sign up to request clarification or add additional context in comments.

Thanks for the info. I should add a few note on this: 1. enum windows's store with "ROOT" (not "CA"). 2. You need to add the cert before connect/handshake, and verify after connect/handshake, otherwise verification will fail.
Problem with this method is that it only verifies the intermediate certificate. It does not verify if your intermediate certificate is not directly trusted but rather their root is trusted. How can we verify that if the root is trusted but not the intermediate certificate?
34

For those of you still struggling with this as I have been, here is a sample code to get you started:

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <cryptuiapi.h>
#include <iostream>
#include <tchar.h>

#include "openssl\x509.h"

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

#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

int main(void)
{
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = NULL;
    X509 *x509;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(NULL, L"ROOT");

    if (!hStore)
        return 1;

    while (pContext = CertEnumCertificatesInStore(hStore, pContext))
    {
        //uncomment the line below if you want to see the certificates as pop ups
        //CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT, pContext,   NULL, NULL, 0, NULL);

        x509 = NULL;
        x509 = d2i_X509(NULL, (const unsigned char **)&pContext->pbCertEncoded, pContext->cbCertEncoded);
        if (x509)
        {
            int i = X509_STORE_add_cert(store, x509);

            if (i == 1)
                std::cout << "certificate added" << std::endl;

            X509_free(x509);
        }
    }

CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
system("pause");
return 0;

}

Your usage of d2i_X509 is invalid because d2i_X509 increments *in argument(you use the cast to avoid compiler error). You must use a temp variable to avoid memory issues, like this: const unsigned char *encoded_cert = win_cert_context->pbCertEncoded; d2i_X509(nullptr, &encoded_cert, ...
pContext will be NULL by the time it is passed to CertFreeCertificateContext() (because the while-loop does not break early), making that call unnecessary.
Note that this answer creates a new store with X509_STORE_new(), if you want to get hold of an existing one you need to use SSL_CTX_get_cert_store() as mentioned in another answer.
What is the use of MY_ENCODING_TYPE ?
5

In OpenSSL 3.2+, replace

SSL_CTX_set_default_verify_paths(ctx);

with

SSL_CTX_load_verify_store(ctx, "org.openssl.winstore://");

This loads certificates from the Windows system store.

Comments

4

No. Not out of the box.

No it is not possible out of the box. It would require additional programming. With OpenSSL you have two (out of the box) options:

  1. Use OpenSSL's own cert store (it is a hierarchy of directories created by perl script provided with OpenSSL)
  2. Use only a certificate chain file created by you (it is a text file with all PEM-encoded certificates in a chain of trust). Creating such a file is easy (just appending it)

Might it be possible to create a PEM file in memory from the windows pkcs files? Then load that? I found: marc.info/?l=openssl-users&m=119583966725315 which describes creating a PEM file from the pkcs packages... And stackoverflow.com/questions/5052563/… describes a mechanism of load a PEM file from a buffer... ?
Yes, that is certainly possible.
Yuck... For some reason, it doesn't like my file (made with the code from that page)... I would have thought someone before me would have done this?
I'm curious if you can comment on the recent solution from @Tushar Sudake... Do you think still this is not possible with OpenSSL?
Yes, I think that might work. Besides, he wrote that he already did it and it worked. When I wrote that it is impossible I meant 'impossible directly' (e.g. OpenSSL using directly certificates from Windows).
4

Yes - in version 3.2+

In openssl version 3.2 it is available using -CAstore org.openssl.winstore://:

c:\Developing> openssl verify -show_chain "c:\Temp\E6.pem"
C=US, O=Let's Encrypt, CN=E6
error 20 at 0 depth lookup: unable to get local issuer certificate
error c:\Temp\E6.pem: verification failed

c:\Developing> openssl verify -CAstore org.openssl.winstore:// -show_chain "c:\Temp\E6.pem"
c:\Temp\E6.pem: OK
Chain:
depth=0: C=US, O=Let's Encrypt, CN=E6 (untrusted)
depth=1: C=US, O=Internet Security Research Group, CN=ISRG Root X1

Nice, but how to do it in C++?
3

Yes

It is possible to use OpenSSL for operation-as-usual, and use CryptoAPI only for the certificate verification process. I see several threads around here on this topic, and most are tiptoed around/through.

With CryptoAPI you have to:

  • decode PEM to DER with CryptStringToBinary(),

  • create a CERT_CONTEXT object with CertCreateCertificateContext()

  • and verify the certificate in this form by well known/documented procedure. (For example here at ETutorials.)

    For last step to work, you also need to initialize HCERTSTORE for one of MY, ROOT, CA system stores, or iterate through them... depending on the behavior you want.

This actually helped me a lot, with this patch here: github.com/adamdruppe/arsd/commit/… my thing seems to work pretty reliably to agree with the browsers on certificates.

Your Answer

Draft saved
Draft discarded

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.