WiFiNINA: TLS fails to local server after cert upload

I’m using an MKR WiFi 1010 with NINA firmware 1.5.0 and WiFiNINA (tried 1.8.14 and 2.1.0).

I need WiFiSSLClient to connect to a device on my LAN at IP:8883 (MQTT over TLS). The device uses a private CA, not a public one.

What works:

  • WiFi connects fine
  • Plain WiFiClient TCP to that IP:8883 works (port is open)
  • WiFiSSLClient to arduino.cc:443 works

What doesn’t:

  • WiFiSSLClient.connect(IPAddress, 8883) to the device → fails
  • WiFiSSLClient.connect("192.168.86.71", 8883) → fails

I used Tools → WiFiNINA Firmware/Certificates Updater (FirmwareUpdater sketch on the board, upload certs). I added the device as 192.168.86.71:8883. The tool says “Certificates uploaded” successfully. I also tried uploading from a local test server on my PC — same result: updater OK, WiFiSSLClient still fails.

From my PC, Python/paho-mqtt connects to the same device with TLS using the LAN access code — so the server is fine.

Question: After a successful cert upload, what is the correct way to make WiFiSSLClient trust a private CA on a local IP (no DNS hostname)? Or is TLS to private/local servers simply not supported on NINA 1.5.0?

Thanks.

Is any message printed when it fails?

The AI summary for "tls cert with ip address does not work" is decent; e.g.

That's the leaf cert on the server. You might have better luck setting up a local DNS name for the server, and issue a leaf cert with that name.

Separately, upload the root cert for your private CA.

I don't actually have a MKR WiFi 1010 board, but with one selected in IDE 2.3.6 here, under the Tools menu, the menu item is named Upload SSL Root Certificates. Do you have to have the FirmwareUpdater sketch running on the board?

So that should use the "desktop-class browser" code in the IDE to go there and grab the cert chain, which should be up to but excluding the root. That last intermediate is issued by a trusted root -- already added to your browser/OS trust store -- to complete the chain. That's your private CA's cert. The uploader sends that root, as the name of the menu item says.

If that already uploaded successfully, should not have to do it again.

Yes, on failure WiFiSSLClient.connect() just returns false. WiFiNINA gives the sketch no BearSSL error code, so I only get the boolean (plus my own timing/WiFi.status print).

On the cert upload: I didn't use the FirmwareUpdater sketch or the Tools menu item — I used the arduino-fwuploader CLI (certificates flash), which puts the board in passthrough and flashes the module itself. It reported "Upload completed".

Tested NINA 1.5.0 and 3.0.1 — same result. TCP to the printer at 192.168.86.71:8883 is fine; TLS fails by IP, by IP-string, and by the serial as hostname. The leaf cert's CN is the printer serial, issuer is the private "BBL CA", no IP in the SAN — so your IP-in-SAN point applies.

One thing I noticed: after flashing only that private CA, arduino.cc and sha256.badssl.com (public, worked before) also started failing — so flashing certs replaces the whole trust set, and TLS 1.2 against the private CA still fails regardless of how I address the host.

Is there a supported way to trust a private CA on TLS 1.2 for a local IP host, or to read the SSL error back from NINA? Re-issuing a leaf with a local DNS name isn't an option — the printer cert is fixed.

PS, I am proudly using Opus 4.8 to help me plan, write code, debug and more.

Thanks!

I see this information in the Arduino Firmware Uploader documentation:

https://arduino.github.io/arduino-fwuploader/latest/usage/#certificates

Thanks, and a quick note, the link for the certificates is now https://github.com/arduino/nina-fw/blob/master/data/cacrt_all.pem (data/roots.pem no longer exists on master).

The latest WiFiNINA library is 2.1.0. Version 2.0.0 added

That's the base class. See if that says anything. Note however, that BearSSL purports to support dNSName as a SAN, but does not say the same for IP SANs.

Note that final bit about "fails safe". So you may be out of luck.

I wish I'd known that before I went with the cloud solution over the printer's local MQTT channel. The device is now in use and works lovely, so motivation has dropped a bit. Either way, thanks for the useful tip, I'll give it a try sometime in the future.

Opus 4.8 asked me to add:

For anyone landing here later: WiFiClient::errorCodeBearSSL() (WiFiNINA ≥ 2.0.0 + recent NINA firmware) is the way to get the actual BearSSL error code instead of just a failed connect.

Thanks for bringing the broken link to our attention. I have now submitted a fix on your behalf: