How do I use Let's Encrypt Certs with Boost.ASIO?

How do I use a DHE group with Apache?

I found the Ciphersuites, but what do I do with that whole thing? Is what I need just before each colon?

ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP

You would use that string as [all in one line]:
SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP

Without quotes or with quotes? Right now I have this:

<VirtualHost *:8000>
    ServerAdmin osmanzakir90@hotmail.com
    ServerName dragonosman.dynu.net
ServerAlias www.dragonosman.dynu.net
    ErrorLog "logs/dragonosman.dynu.net-error.log"
    CustomLog "logs/dragonosman.dynu.net-access.log" common
ProxyPass "/" "http://192.168.10.14:5501/"
ProxyPass "/?q=accesskey" "http://192.168.10.14:5501/?q=accesskey"
SSLCertificateFile "C:/Users/Osman/.acme.sh/dragonosman.dynu.net/fullchain.cer"
SSLCertificateKeyFile "C:/Users/Osman/.acme.sh/dragonosman.dynu.net/dragonosman.dynu.net.key"
SSLEngine On
SSLProtocol "TLSv1.2"
SSLCipherSuite "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP"

That should work…
Have you tried it?

I removed the port forwarding rule for port 5051 on my router and added a rule for port 8000, and then I changed my .cpp code file back to the non-SSL version and recompiled it. But when I run the app, start Apache as a service, and try to visit https://dragonosman.dynu.net:8000/ , I get an error page saying that Edge can’t reach that page.

By the way, what do I with this?

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

It’s the default setting.

My app itself is listening on port 5501, so I need set up the proxy so that requests sent to port 8000 are proxied to port 5501.

That is how you configured it:

hmm...
From that same system:
Try:
curl -Iki http://192.168.10.14:5501/
curl -Iki https://192.168.10.14:8000/

[assuming the local IP is 192.168.10.14]

Visiting 192.168.10.14:5501 in my browser works, but 192.168.10.14:8000 doesn’t.

And on Command Prompt I get this:

curl -Iki https://192.168.10.14:5501
curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed
curl -Iki https://192.168.10.14:8000
curl: (7) Failed to connect to 192.168.10.14 port 8000: Connection refused

That test should be without the "S"
curl -Iki http://192.168.10.14:5501

Please show local IP:
ifconfig | grep -Ei 'add|inet'

Without the S, the test for port 5501 is stuck. Nothing’s happening.

ifconfig isn’t recognized, but running ipconfig gives me this:

Wireless LAN adapter Wi-Fi:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::ce:d32:bd96:79e4%9
   IPv4 Address. . . . . . . . . . . : 192.168.10.14
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : fe80::e6a:bcff:fef6:a306%9
                                       192.168.10.1

Also:

curl -Iki http://192.168.10.14:8000
curl: (7) Failed to connect to 192.168.10.14 port 8000: Connection refused

The other one is still frozen, so I did this in another Command Prompt window.

What do you think could be the problem if the reverse proxy itself is configured correctly?

After doing this in the ProxyPass directives:

ProxyPass "/" "http://dragonosman.dynu.net:5501/"
ProxyPass "/?q=accesskey" "http://dragonosman.dynu.net:5501/?q=accesskey"

I get this:

curl -Iki http://dragonosman.dynu.net:5501
curl: (7) Failed to connect to dragonosman.dynu.net port 5501: Connection refused
curl -Iki http://dragonosman.dynu.net:8000
curl: (7) Failed to connect to dragonosman.dynu.net port 8000: Timed out

I changed it back to the IP address in the .conf file.

@rg305 Because I’m having trouble with the reverse proxy anyway, I’m still going to try manually loading the server certificate into app directly via Boost.Asio itself. I’ve changed the C++ code for my server app to follow the sync HTTP server example for Beast that’s using SSL. I think it’s possible that I need to do this to get it to work with the reverse proxy too.

Also, how do I get the certificate password? Boost.Asio has a password callback method on the SSL context object, and the Beast example does it like this:

ctx.set_password_callback(
    [](std::size_t,
        boost::asio::ssl::context_base::password_purpose)
    {
        return "test";
    });

The callback must return the password as a string, and the samples use “test” as the password. And the example HTTP client (sync) example also has root certificates. I have code where I’m sending a request to the currency API that I’m using to get conversion rates, but I’m sending the request over port 80 because SSL isn’t allowed in the free plan.

Anyway, here’s the link to the app source code repository on GitHub again. The .cpp file is using the HTTP SSL server example (sync) and the HTTP non-SSL client (sync) example. If there are mistakes you can see, please tell me about it and help me fix it. Thanks in advance. [Hopefully there are others who can help me if you can’t for any reason.]

Edit: It works. I have HTTPS on the app now. I just need to fix the URL I’m sending CORS requests to in my JavaScript code in the app. I do have an error on the read call here that says that the stream was truncated:

// Read a request 
http::request<http::string_body> req;
http::read(stream, buffer, req, ec);
if (ec == http::error::end_of_stream)
{
	break;
}
if (ec)
{
	std::cerr << "Lines 591 and 592:\n";
	return fail(ec, "read");
}

I’ll try asking about this in the Boost users mailing list too, but what is this about and is it possible to fix it?

@rg305I found a currency API that accepts requests over HTTPS in the free subscription. I already know how to use the SSL certificate with the server, but I don’t know how to get the root certificate for the client. The Boost.Beast examples include an SSL-enabled synchronous HTTP client and it uses a root certificate. Here’s the header file root_certificates.hpp on GitHub and this is the C++ file using that header.

Anyway, I still need to know how to get the password.

The root cert is never provided by LE nor should it be included in the servers' reponse.
The most you can get (by default) is the complete chain.
You can use something like this to see the cert and chain:

openssl s_client -connect community.letsencrypt.org:443 -servername community.letsencrypt.org -showcerts

You will have to get creative to get the root cert from the (tail of) that chain.
[but that should NOT be required; as root certs are already included in all such packages]

Please show:
netstat -natp tcp
[only need to see the LISTENING lines]

I need it for the client code that’s making a request to the currency API in the C++ code. The Boost.Beast example uses a root certificate for the encryption. I’ll try running that command.

@rg305 I just tried the command you suggested. The result is:

openssl s_client -connect community.letsencrypt.org:443 -servername community.letsencrypt.org -showcerts
CONNECTED(00000148)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = community.letsencrypt.org
verify return:1
---
Certificate chain
 0 s:CN = community.letsencrypt.org
   i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
-----BEGIN CERTIFICATE-----
MIIFaTCCBFGgAwIBAgISA/X28NHnyxeqPImiXq/ju/AdMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAxMjcwMDAwMDdaFw0x
OTA0MjcwMDAwMDdaMCQxIjAgBgNVBAMTGWNvbW11bml0eS5sZXRzZW5jcnlwdC5v
cmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC36JOQ31VvVWxnNIzh
yKMwlRFLOjsAqovVNIfQByl11nDSdLBsxcRMSvoSGq0OmsumsnryO3KY0XDYCHt4
Os9nhlKW8cCcxDsGSNcgIYOJV4wH1yEOYdQRdlEzKjlOk6jLEQcV5OEEUdSaGmSF
tKCyBrUX6IpAP7K9eK3eYpqktXDGeaVc8JuiURhvkb8U3hlrm9qLr3AsYJ2q5/Ps
iSJ90QgIkgLySEE2WOegc2nnDslngIza4W8oHEMFo3Mf/ZAxbcEamT9y1sVuEr8H
Rg8M3+qEkBIZPINFu/ov+WC6yUuQXL4MFUiRCMmI+rwxYmHxpeXknlaKbV7Cw7BN
JOLzAgMBAAGjggJtMIICaTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFGZ6hT/TrZ1A
pn54WYsAtvtASWhbMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8G
CCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxl
dHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxl
dHNlbmNyeXB0Lm9yZy8wJAYDVR0RBB0wG4IZY29tbXVuaXR5LmxldHNlbmNyeXB0
Lm9yZzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQMGCisGAQQB1nkC
BAIEgfQEgfEA7wB2AHR+2oMxrTMQkSGcziVPQnDCv/1eQiAIxjc1eeYQe8xWAAAB
aIzSIY8AAAQDAEcwRQIgEBLjfOodSdmx2PlUrvn/4zs15ZPYfT90mkCUQ9fyRKcC
IQDxC2BCAn+bMSrJEJGR0gH+Fr5ew42+8D7zdM2Cl9hg9wB1ACk8UZZUyDlluqpQ
/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABaIzSIZoAAAQDAEYwRAIgF+VrbpZfb0e7
V73xnOUvaTO54sE/OtHg8JCOqt5iSRMCIF6H6BdfV48RFjvSbHLv9DMqdEhjNUth
7KMcoPj84OtTMA0GCSqGSIb3DQEBCwUAA4IBAQBJIFKMsBZXPIIt5CC1sigxzjBy
cTbNkjvaHs+Xzuvq6alc52oBMsBU5l+4Qvr+bpfZ1v6JTYZXuoyr3pKMyZRPtOL3
Y4y0FRVjL8l4cdewKSRuzZZBKY9zU4M0daIefjT42ohbrsiRuoGx/N95enZfNvBj
d9rLRSmtIdrqf7FlwaR7d6KnbsqQSxFZdSV7Ds4qAfMvRI7EJ/uk8UdCKXrxoLVB
bKtdXoxrP7Y0mRl5x2rccCZhVjDmUukk950cQNkltgQ99MYus+bB2uNrOkRlNqws
7IQ0usAg2AsNzy0pU5RPgDITSiEU/D9h3gxel4QeVI1JmNoeiW7rdhQbPBX0
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
---
Server certificate
subject=CN = community.letsencrypt.org

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA512
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3077 bytes and written 453 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: E82398218D3F68AB9344A4646A2FDA65379813077741017CA640515FE81E8F9D
    Session-ID-ctx:
    Master-Key: 5F12660C7CA292B29E26CA1709947C1AC17A66F30348E5B2237F06C9116AF615012541850BF1C75DCA86CF7CE88AFCFA
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1552605120
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
---
HTTP/1.0 408 Request Time-out
Cache-Control: no-cache
Connection: close
Content-Type: text/html

<html><body><h1>408 Request Time-out</h1>
Your browser didn't send a complete request in time.
</body></html>
closed

Is that meant to be the root certificate chain?

I need to make a GET request over HTTPS in my C++ code. I’m sending the request over port 443, but it doesn’t seem to be enough because I’m getting an empty response body. This is the root certificate header and this is the C++ file using it, in the Boost.Beast examples that I have to use as a reference.

Do I use MS Edge’s root certificate if I can find it? Because I don’t know how to find it. I’ve tried searching around for into on root certificates but I can’t find anything. Is it in the certificates I got from Let’s Encrypt when I got the server certificates? And if so, which one is it?

Edit: It does mention this is the root certificate, but do I need both of these or just one?

Edit2: This chain isn’t the same one I got for the server. I checked (by reading fullchain.cer into a file and writing an if condition to match the strings). Neither of them is the same as the one in dragonosman.dynu.net.cer either.

The Boost example is bad because it hard-codes a single GeoTrust trust anchor (root) into your application. This is the wrong thing to do in a real application. If you are writing a client that has to do x.509 verification, then you are meant to load the trust anchors from the operating system. These get updated from time to time, and you have to keep up to date with them.

On Linux, you would call ssl::context::add_verify_path on the standard directory for the distribution, e.g. /etc/ssl/certs/.

On Windows, I don’t know what the standard approach for this is. It’s pretty clear from the output of your openssl s_client command that your OpenSSL installation doesn’t know how to locate the root certificate store either. So you might have to get it from https://curl.haxx.se/docs/caextract.html, or alternatively identify what the correct way to do it in Microsoft-land is.

Which certificate do I use out of those two as the root certificate, though? It mentions the words “CN = DST Root CA X3”, so is the one right above that the root certificate I need (because below the certificate that’s below that one, it says “Server Certificate”).

Anyway, I’ll try following the link you posted for curl.haxx.se.

Choosing just one root to load is straight up wrong. Let’s Encrypt isn’t going to use DST Root CA X3 forever - it’s expiring rather soon and your application will break. Or it might get revoked.

You could just load DST Root CA X3, and it will work for a while sure, but it’s bad.

I checked, and on Windows you are meant to use CertOpenSystemStore(0, "ROOT") and load each root certificate from there. Or you can bundle cacert.pem with your application and load each certificate from there.

This type of thing doesn’t give you portable code, but if you’re using a library that is barely one level above from using raw OpenSSL, then you’re going to have to deal with it.

e: updated wrong link

Is there no way I can find out where the root certificate store is on Windows so I can enter that path when using the OpenSSL command to find the root certificates? And a cross-platform way to get the certs from the store if one exists.

In the documentation for CertOpenSystemStoreA (which is the function whose documentation you linked to) it says that it returns a handle to the cert store if it succeeds and NULL if it fails. So if it succeeds and gives me a handle, I need to know how to get the certificate out of the handle. I don’t know where to look for the info, so I’d try to ask on either the Microsoft Community or MSDN. But as I said before, I’d rather just use a cross-platform way so that my app can work no matter where the server app runs (even though I’m going to host the app on my own laptop only).

If you want this to be cross-platform, then individually passing each certificate from the curl cacert.pem bundle (which you’d have to include with your application) to ssl::context::add_certificate_authority would probably do a trick. But you take responsibility for updating that file over time.

The proper Windows way, you need to learn the Windows Crypto API. There is no root store on the filesystem. Use CertEnumCertificatesInStore on the store handle to loop over each entry, extract the certificate bytes, convert to PEM, pass it to the Boost SSL context … etc.

How often does Mozilla update the cacert.pem file? I could just try to stay up to date with Mozilla. I can just load the cacert.pem file into my app and use a loop or something to load each certificate into the Boost SSL context, right? I’ll just do it that way.

And if I can, I should probably write code to make a GET request to the place where the cacert.pem file is. That way, I could just get the updated file when it changes.