pycom / pycom-micropython-sigfox

A fork of MicroPython with the ESP32 port customized to run on Pycom's IoT multi-network modules.
MIT License
196 stars 167 forks source link

v1.20.0rc4: expired certificate in Pybytes CA chain #247

Open noahwilliamsson opened 5 years ago

noahwilliamsson commented 5 years ago

Hey @Xykon, in commit 978c9bbd835112c1a0f200f69d448dabebe9f846 you appear to have committed an expired Let's Encrypt leaf certificate (forpybytes-staging.pycom.io) in the file https://github.com/pycom/pycom-micropython-sigfox/blob/release-candidate/esp32/frozen/Pybytes/_pybytes_ca.py.

For reference, here's an excerpt of leaf certificate in question:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:76:f8:94:b9:36:36:e6:f0:3e:f5:3e:9e:46:ed:3e:55:a9
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
        Validity
            Not Before: May  7 15:04:08 2018 GMT
            Not After : Aug  5 15:04:08 2018 GMT
        Subject: CN=pybytes-staging.pycom.io

(if you're on Linux or a Mac, you can use openssl x509 -noout -text -in <certificate-file.pem> to show the above text from a certificate file in PEM format)

While I haven't looked at the implementation for this specific case, the way things typically work is that you include one or more root CA (certificate authority) certificates in the the device or the computer. These certificates serve as a trust anchor on the device.

So let's say that a Pycom device would want to establish a TLS connection to https://pybytes.pycom.io (or some other protocol protected with TLS). This server would then present a certificate chain consisting of:

After verifying that the leaf certificate has not expired and is also valid for the domain the device intended to connect to, the Pycom device would go over each certificate presented by the server and verify that it was issued (signed) by the next certificate in the chain (using public key signature checks) . To verify the last certificate in the chain the device needs a local copy of the issuing (signing) certificate -- this is what need to be present in the device's trust store (= a list of trusted CA certificate a.k.a. the trust anchor).

On most operating systems and (some) browsers, the trust store contains a list of over hundred vetted CA certificates, mostly from commercial CAs and government organizations, but also from Let's Encrypt.

Naturally it's not feasible to include a large list of root CAs on a device with small amounts of storage but if you only intend to use certificates from Let's Encrypt on your servers you can get away with keeping just one or two root CA certificates in the device's trust store:

Of those above, the most important certificate for now is the DST Root CA X3 one, which has signed the intermediate certificate that Let's Encrypt uses to issue most of their leaf certificates from. For best/future compatibility you should also include their own root certificate, the ISRG Root X1. The way you do this is to simple concatenate these two files and place them in _pybytes_ca.py. Unlike a server certificate chain, the order is not important here.

You can read more about Let's Encrypt's CA certificates on https://letsencrypt.org/certificates/.

All of this of course assumes that your servers are setup correctly and serve up not only the leaf certificate, but also any intermediate certificates that leads up to a root certificate present on the connecting device.

The web server at https://pybytes.pycom.io is correctly setup and serves up both a leaf certificate valid for pybytes.pycom.io (among other domains) as well as the issuing (signing) certificate CN=Let's Encrypt Authority X3 (which in turn was signed by a root CA present in most operating systems' trust stores).

The web server at https://pybytes-staging.pycom.io is however misconfigured and lacks the intermediate certificate CN=Let's Encrypt Authority X3. If you update the certificate chain and include the missing intermediate certificate, you could drop the two first certificates (expired leaf + intermediate cert) in esp32/frozen/Pybytes/_pybytes_ca.py, and optionally consider adding the ISRG Root X1 certificate. Then things should work as expected.

(if your TLS stack supports it, you might want to consider encoding your certificates in the binary DER format as opposed to the text-friendly PEM format: header+base64 encoded cert+footer).

Use the excellent https://www.ssllabs.com service and verify that it no longer reports Chain issues: incomplete for https://www.ssllabs.com/ssltest/analyze.html?d=pybytes-staging.pycom.io once you've updated the web server(s) responsible for pybytes-staging.pycom.io.

Alternative, if you're on a Linux/Mac and have a local copy of the DST Root X3 in a file named dst_x3.pem, you can use the openssl utility like this:

openssl s_client -connect pybytes.pycom.io:443 -servername pybytes.pycom.io -showcerts -CAfile dst_x3.pem 
openssl s_client -connect pybytes-staging.pycom.io:443 -servername pybytes-staging.pycom.io -showcerts -CAfile dst_x3.pem 

The last line should read Verify return code: 0 (ok) if the server's certificate chain could be verified sucessfully. If it's broken, the last line will say something like Verify return code: 21 (unable to verify the first certificate).

HTH.

Xykon commented 5 years ago

Thanks a lot for reporting this... I'll ask the team to generate a new pem file that I can include in the next release.

noahwilliamsson commented 5 years ago

@jirikrepl @Xykon If you fix the certificate chain issue on pybytes-staging.pycom.io you can drop Let's Encrypt's issuer certificate (the first cert in _pybytes_ca.py) that is still included in f1e26760b236ea35a6d994a35076f91501b761bb (pybytes-master branch), leaving only the root CA certificate from IdentTrust.

Since it appears this server is using Nginx, what you need to do is to append the above mention certificate in the file pointed out by the Nginx directive ssl_certificate and then make Nginx reload its configuration (usually sudo systemctl reload nginx).

eflorent2020 commented 5 years ago

Hi @noahwilliamsomm good catch ! A PR that do exactly what you have explained is on the go on an upstream repository. Will be merged on by build pipeline.