Home > Mobile >  C/C Validating Host and Peer with CURL for CloudFlare based websites
C/C Validating Host and Peer with CURL for CloudFlare based websites

Time:10-27

I`m working on an API behind CloudFlare and I would like to validate the connection fully for extended security. The platform I am using right now is Windows 10.

First I downloaded some CA's found on CloudFlare's website (Cloudflare_CA.pem, origin_ca_rsa_root.pem, origin_ca_ecc_root.pem) and then tried to contact the API after settings the required options in CURL:

struct curl_blob blob;

std::string cf_pem = ...;

blob.data   = cf_pem.data();
blob.len    = cf_pem.size();
blob.flags  = CURL_BLOB_COPY;
curl_easy_setopt(pcurl, CURLOPT_CAINFO_BLOB, &blob);

/* Set the default value: strict certificate check please */
curl_easy_setopt(pcurl, CURLOPT_SSL_VERIFYPEER, 1L);

/* Set the default value: strict name check please */
curl_easy_setopt(pcurl, CURLOPT_SSL_VERIFYHOST, 2L);

All failed with PEER validation error. Then I have tested with openssl the following:

openssl s_client -connect website:443

The result was:

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)

Afterwards I found a very nice command which basically "extracts" the CA from the given website:

openssl s_client -showcerts -servername website -connect website:443 > cacert.pem

At the end I still received:

Verify return code: 20 (unable to get local issuer certificate)

However the cacert.pem was created with some content inside it. I grabbed the first certificate between then -----BEGIN CERTIFICATE-----/-----END CERTIFICATE----- and put it inside the code.

Validation succeeded and CURL did not complain anymore. However, I am unable to contact other hosts under CloudFlare which means this CA is not "ok".

So my question is, what to do? How I find the correct CA for CloudFlare?

Please advise, I`m sure other developers might face the same issue.

CodePudding user response:

Looking at a typical Cloudflare hosted site, the certification path ends with a root CA of "DigiCert Baltimore Root".

Cloudflare Root CA

In Windows 10, should be able to find this cert under Local Machine Certs -> Trusted Root Certification Authority -> Certificates -> Baltimore CyberTrust Root.

Or alternatively, download from the source (will probably want to also verify fingerprints if you use this method) - https://www.digicert.com/kb/digicert-root-certificates.htm

CodePudding user response:

As @David mentioned, it is correct that CloudFlare uses DigiCert Root CA (in general). However, as they state on their website this can change; which means if you hard code such check your software might break in future at any point.

Without further due the options are as following:

Option A

Buy an "Advanced" certificate from CloudFlare instead of the Universal one, and select your desired Root CA. This ensures (up to a point) that it will be the same and you can validate any domain using that CA (in my case I tested with DigiCert Root CA).

Option B

Use mTLS feature (mTLS is a great way to protect your API). This is preferred as a long-term solution however the downside is that you need to supply/embed the certificate private key as well in your software. You can however revoke at any point the certificate if it was compromised.

For my choice I want to also validate the CA however using the DigiCert Root CA does not work for mTLS, and it's pretty clear because on your dashboard you will see:

Review Client Certificate for CN=Cloudflare, C=US
Validity Period: 15 Years
Authority: Cloudflare Managed CA for ....

I could not find/download the root certificate for "Cloudflare Managed CA". I`m not happy with this so to ensure everything works OK I have used the "Baltimore CyberTrust Root" from here: https://baltimore-cybertrust-root.chain-demos.digicert.com/info/index.html

Now you can have mTLS with CURL with CA validation as well:

// https://baltimore-cybertrust-root.chain-demos.digicert.com/info/index.html
std::string ca_pem = ...

// mtls.cert
std::string cert = ...

// mtls.key
std::string key = ...

struct curl_blob blob;
blob.data   = cert.data();
blob.len    = cert.size();
blob.flags  = CURL_BLOB_COPY;
curl_easy_setopt(pcurl, CURLOPT_SSLCERT_BLOB, &blob);
curl_easy_setopt(pcurl, CURLOPT_SSLCERTTYPE, "PEM");

blob.data   = key.data();
blob.len    = key.size();
curl_easy_setopt(pcurl, CURLOPT_SSLKEY_BLOB, &blob);
curl_easy_setopt(pcurl, CURLOPT_SSLKEYTYPE, "PEM");

blob.data   = ca_pem.data();
blob.len    = ca_pem.size();
curl_easy_setopt(pcurl, CURLOPT_CAINFO_BLOB, &blob);

IMPORTANT NOTES REGARDING mTLS:

  • Make sure you input effort to encrypt your cert/key if you embed it inside your software.
  • I do not recommend you to supply your cert/key as individual files next to your software.
  • Even if you encrypt your cert/key inside your software there is always a way to get it, either via unpacking/de-onfuscabtion/de-virtualization; either during runtime... etc. My advise is for you to immediately re-encrypt the data or delete it from memory ASAP.
  • In case someone gets your cert/key, first generate a new cert/key then update your software and afterwards revoke the existing one. Then you should look into how someone got your cert/key and try to mitigate the issue.

Please feel free to comment if you disagree with anything and/or/if you have some suggestions/improvements.

  • Related