HTTPS connection to specific sites fail with cURL on macOS
Root CA certificates used by the mentioned sites (Comodo and USERTrust) have expired this morning (UTC time). While I find it remarkable that two different Root CA certs would expire at the exact same second, this may be explained by USERTrust being affiliated with Comodo (now Sectigo).
Edit: These two actually never were Root CA certificates, but rather intermediate CAs signed by "AddTrust External CA Root". Therefore, their expiration date was determined by the validity of the "AddTrust External CA Root" certificate, which also happens to have expired at the exact same second.
Now, updated certificates (sharing their private keys with the expired ones) have been issued back in 2010 (Comodo, USERTrust). These certificates are part of the common Root CA stores these days (including Apple's system trust store), therefore browsers establish the connections perfectly fine. The same is true for most variants of cURL (e.g. from MacPorts or Homebrew), which are built against custom OpenSSL installations.
The built-in cURL variant of macOS 10.14 is built against LibreSSL and uses /etc/ssl/cert.pem
as its Root CA store, which also includes the new certificates. However, something appears to causing cURL or LibreSSL to prefer the old certificates for its validity check. I suppose cURL is at least somewhat involved in the problem, since I couldn't get the connections to fail using /usr/bin/openssl s_client
(/usr/bin/openssl
is actually built from LibreSSL).
My hypothesis would be that the problem is caused by the sites sending the expired Root CA certificate as part of their certificate chain. Including the Root CA in such chains is allowed, but not required, and in this case appears to break certificate validation.
Edit: This is part of a series of issues around the "AddTrust External CA Root" expiration. See this blog post by Andrew Ayer or this Twitter thread by Ryan Sleevi for the bigger picture. Ryan Sleevi also has a collection of things failing due to the expiration.
On macOS 10.15, where cURL uses OpenSSL 0.9.8 by default, the issue apparently may be mitigated by setting the environment variable CURL_SSL_BACKEND=secure-transport
. This does not work on 10.14 with its LibreSSL which, according to Christian Heimes, is affected by the issue in general.
Just comment out the AddTrust
entry in /etc/ssl/cert.pem
, since the end certificates are cross-signed, they will be validated against USERTrust
.
In theory there should be no need to comment out that entry, but in practice, the LibreSSL version that ships with mac (2.8.3 on Catalina) has broken certificate path validation because it's based on an older version of OpenSSL that also contains the same bug (< 1.1.1).
According to the LibreSSL documentation (https://www.libressl.org/releases.html), they started incorporating OpenSSL 1.1.1 functionality in their 3.x.x series, I could find a way to update it manually but I am lazy and will wait for Apple to fix it.
All these sites that I have found seems to have the same expired CA cert in their chain:
openssl s_client -connect kapeli.com:443
CONNECTED(00000003)
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify error:num=10:certificate has expired
notAfter=May 30 10:48:38 2020 GMT
I see issues popping up on many different sites about this right now. Editing the ca cert file like @jmibanez suggests will probably work when the site isn't sending the expired certificate in the response. I tried the latest CA cert file from https://curl.haxx.se/ca/cacert.pem using curl --cacert path/to/cacert.pem
which didn't work. Browsers seems fine so they seem to ignore the expired CA cert included in responses from web sites.
EDIT: My bad here. I was using curl 7.54 by mistake. Newer versions are working. The error does not exist when using curl 7.67/7.70.