LilyGo TTGO ESP32: update root certificate at runtime

I have a LilyGO SIM7000E. My project will send data periodically to a specific server. Just this one. In the planned end state there will be quite some modules running on solar power in remote places sending data over the mobile network and it would be an absolute hassle to update them on site.

A first prototype is working fine using WiFi via WiFiClientSecure and https requests. Mobile network implementation will come later.

My issue: I have the root CA of the the server storing the data hardcoded in the sketch. The root CA expiration date is in a little over 10 years but still I do not like the fact that at that point in time all modules will stop working. I therefore would like to periodically update (once every month?) the certificate at runtime and store it in SPIFFS. As far as I understand, the new root certificate will be published quite a while before the old one expires and therefore fetching the new certificate using the old one should not be a problem. I hope I got this right.

With regard to updating the root CA at runtime:

  1. Is this doable by the ESP32 directly accessing my server's root certifcate?
  2. If the above isn't possible, if I store the root certificate on my server as a (text) file, I can't see how the ESP32 couldn't download that (text) file via https? Since the root CA public key is what it says "public", I can't see any reason why storing the root CA as a file on my server could be of a security concern?

(For 2. to work I obviously need to make sure that the new root CA is stored on the server before the old one expires. This could be automated on the server side.)

TIA

I am not very well versed in this stuff, but what is the purpose of these certificates?

In order to establish a secure connection to my server (https).

A little OCD?

You keep referring to it as "my server". If it's "your" server, why not simply refrain from updating the certificate altogether?

Moreover, why do you need an https connection to begin with? What makes you think anyone is interested in snooping on your data transfers?

1 Like

Encryption is standard nowadays and good practice. Snooping is not an issue, inserting random data in a massive database however is. My project has a planned lifespan of way over 10 years so nope, no OCD. My server runs a Let's Encrypt certifcate. Can we now please get back on topic, thank you.

Doing (1) won't help, but you can do (2).

But consider that the ESP32 WifiClientSecure has the setInsecure method. When enabled, you don't need a CA cert, and the call still works. The connection is encrypted.

SSL/TLS prevents the server from being spoofed. If you ask for example.com, you get a leaf certificate for that site. You the client can verify a digital signature of that leaf cert that was signed by the CA's private key -- with the corresponding public key, included in the CA cert. setInsecure skips that.

If you're trying to prevent rogue clients from inserting data, server-side TLS by itself doesn't apply. To use certificates for that purpose, you need mutual TLS, which is done in IoT. Each client gets its own certificate, signed by the server organization's own private CA. That way, it is practical to generate hundreds/thousands/billions of device certs that it can verify. And then you can set any expiration you want. Although it seems like a bad idea to go too far with that. For example, AWS sets their IoT client certs to expire at the end of 2049.

Without client certs, you can use more standard authentication methods to prevent rogues. All of these require some effort to dissuade exfiltration of the secrets/certs that have been stored on the device, should someone get physical access to them.

2 Likes

How can the connection be encrypted if there's no way to exchange a session key without a cert to encrypt / decrypt it?

@kenb4 Thank you. Will attempt to do 2), as I believe I cannot use setInsecure when using the mobile network connection.

@gfvalvo because I have a valid hardcoded (or stored in SPIFFS) certifcate to start with. I then want to periodically check if a new certificate is available. If yes, download and store it, reboot, new certificate in use. As far as I understand, the new certificate will be available well before the old one expires.

That wasn't my question. I was asking about @kenb4's statement that an encrypted connection could be established without any certificate.

My (still unanswered) question for @rflt was ... why does your server's certificate need to be updated at all? Isn't it your server? Don't you control it? If so, why can't you just simply give it a long enough "lifetime" to not need updating?

My (still unanswered) question for @rflt was

My domain is hosted by a third party and it's running a "Let's encrypt" certificate which has an expiry date out of my control.

Got it. It's not your server physically, only notionally.

Got it. It's not your server physically, only notionally.

Exactly. Apologies for the unclear phrasing.

During the handshake, the server sends its certificate chain. It includes the leaf cert for the server, and any intermediate certs. It may include the root cert, but the client already has the root CA certs it trusts, so that would be redundant. The leaf cert is used to generate the session key.

Having the root cert is all about trusting the cert chain, which includes whether the leaf is actually for the server you requested. Here's the relevant declaration

void setInsecure();  // Don't validate the chain, just accept whatever is given.  VERY INSECURE!

It doesn't say, "no longer encrypted", which would be worth noting. Here's what curl has to say about their own -k, --insecure option

The cert verification also checks whether any in the chain has expired.

Or at least it's supposed to. I tested expired.badssl.com

$ openssl s_client -showcerts -connect expired.badssl.com:443
CONNECTED(00000003)
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.badssl.com
verify error:num=10:certificate has expired
notAfter=Apr 12 23:59:59 2015 GMT

with the (also expired in 2020) "COMODO RSA Certification Authority" root via setCACert, and the connection was allowed.

It's an entirely client-side check. How would "they" know the difference? Try it.

Which does mean that you should use setInsecure if it works. Rather be aware you're not preventing other "unauthorized" access just by using plain server-side TLS.

Thank you, helps a lot!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.