Using the X.509 certificates of the Windows certificate store via libcurl
Date: Tue, 18 Jun 2019 14:31:06 +0000
Hi all
I'm using libcurl to build the networking layer for a cross-platform app that will run on Android, iOS and Windows. To be able to do HTTPS requests I'm using Mbed TLS 2.16 together with libcurl 7.65 on Android and Windows and Secure Transport in the iOS app. So far it works as expected on both Android and iOS. Getting the configuration right for both of these platforms was all that was required to make it work properly.
However, the situation on Windows is a bit different. The goal is to have a Windows UWP app which means I need to build on top of the Windows Runtime (WinRT). That in terms means that many Win32 API functions are not available due to strict limitations in WinRT. Because of this, it's not possible to use libcurl together with Secure Channel (USE_SCHANNEL define) in Windows UWP apps. The compiler will right away complain about the use of the corresponding Win32 functions.
So, I attempted to just use Mbed TLS on Windows as well. I successfully compiled libcurl and Mbed TLS for WinRT but I'm missing a particular feature to wrap it all up: Utilizing the X.509 certificates that are stored in the Windows certificate store together with libcurl. I think I'm pretty close to actually make this work as well. However, I'm unsure about how to make libcurl use those certificates.
Here's what I got so far:
I wrote a C function that opens the certificate store on Windows and extracts all root certificates from it. It does so for the current user that runs the app which seems to work fine because every user should be able to access all root certificates. The function will then build a chain out of all those certificates which are of type "mbedtls_x509_crt" that is provided by Mbed TLS. The code only relies on Win32 functions that are available for both Windows UWP and native Windows applications and looks as follows:
Header:
#include "mbedtls/x509_crt.h"
#if defined(USE_MBEDTLS) && defined(_WIN32)
#include <Windows.h>
#include <wincrypt.h>
static mbedtls_x509_crt* ca_chain = NULL;
mbedtls_x509_crt* build_windows_ca_chain();
mbedtls_x509_crt* extract_cert(PCCERT_CONTEXT certificateContext);
#endif
Implementation:
#include "<header posted above>"
mbedtls_x509_crt* build_windows_ca_chain()
{
HCERTSTORE certificateStore = NULL;
if(certificateStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"Root"))
{
mbedtls_x509_crt* previousCertificate = NULL;
mbedtls_x509_crt* currentCertificate = NULL;
PCCERT_CONTEXT certificateContext = NULL;
if(certificateContext = CertEnumCertificatesInStore(certificateStore, certificateContext))
{
if(certificateContext->dwCertEncodingType & X509_ASN_ENCODING)
{
ca_chain = extract_cert(certificateContext);
previousCertificate = ca_chain;
}
while(certificateContext = CertEnumCertificatesInStore(certificateStore, certificateContext))
{
if(certificateContext->dwCertEncodingType & X509_ASN_ENCODING)
{
currentCertificate = extract_cert(certificateContext);
previousCertificate->next = currentCertificate;
previousCertificate = currentCertificate;
}
}
if(!CertCloseStore(certificateStore, 0))
{
// TODO: Handle case
}
}
}
else
{
// TODO: Handle case
}
}
mbedtls_x509_crt* extract_cert(PCCERT_CONTEXT certificateContext)
{
mbedtls_x509_crt* certificate = malloc(sizeof(mbedtls_x509_crt));
mbedtls_x509_crt_init(certificate);
mbedtls_x509_crt_parse(certificate, certificateContext->pbCertEncoded, certificateContext->cbCertEncoded);
return certificate;
}
In order to supply the certificates to Mbed TLS via libcurl I *think* I'd need to modify libcurl's "mbedtls.c" file. So, I did a quick test by modifying the call to "mbedtls_ssl_conf_ca_chain" in that file as follows:
Original code:
...
mbedtls_ssl_conf_ca_chain(&BACKEND->config, &BACKEND->cacert, &BACKEND->crl);
...
My modification:
...
#ifdef _WIN32
build_windows_ca_chain();
mbedtls_ssl_conf_ca_chain(&BACKEND->config, ca_chain, &BACKEND->crl);
#else
mbedtls_ssl_conf_ca_chain(&BACKEND->config, &BACKEND->cacert, &BACKEND->crl);
#endif
...
Sending an HTTPS GET request with that code produces error CURLE_PEER_FAILED_VERIFICATION (60) with the error message "Cert verify failed: BADCERT_NOT_TRUSTED". So, obviously the certificates are not properly taken into account during the TLS handshake.
It would be great if someone could point me to what I need to do on libcurl's side in order to properly utilize those certificates. I'd be willing to turn this into a proper pull request because I think it would be generally useful for cURL to be able to better interface with Windows' certificate stores. But in order to do so I need your help.
Thanks and regards
Philip
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.haxx.se/mail/etiquette.html
Received on 2019-06-18