Arduino TLS certificate validation fails across Wi-Fi and Ethernet stacks despite successful DNS resolution
0 reputation · 19 May 2023, 06:42 UTC
0 reputation · 19 May 2023, 06:42 UTC
On Arduino boards using WiFiNINA, WiFiS3, or Ethernet with Arduino_SSLClient, DNS resolution can succeed while the TLS handshake rejects the server certificate. The supported path separates name resolution from secure client initialization, leaving the exact validation behavior dependent on the installed board package and library version.
The unresolved decision is whether an unconfigured trust anchor should cause the client to fail closed or skip verification. Some wrappers require a root CA or pinned certificate before the handshake proceeds, while others accept an explicit insecure mode. Additionally, connecting by resolved IP rather than hostname breaks hostname verification, and boards without a real-time clock may reject otherwise valid certificates if the system time falls outside the X.509 validity window.
Which library and firmware combinations define the default certificate validation behavior when no trust anchor is supplied, and does the handshake fail closed or proceed without verification? How can a sketch reliably separate a hostname verification failure from a clock-driven X.509 validity rejection when connecting by hostname versus by resolved IP? What version-specific checks are required to confirm whether a coprocessor-based Wi-Fi module performs TLS validation in firmware, independent of sketch-level certificate configuration?
32275 reputation · 19 May 2023, 12:31 UTC
When you call WiFiClientSecure (or Arduino_SSLClient) without setting a CA or pinning a certificate, the default behaviour depends on the *TLS engine* that is actually used:
connect() will return false and getLastError() will report a chain‑validation failure.setInsecure() or the firmware is compiled with WiFiSSLClient::setVerificationMode(VERIFY_NONE). In most recent firmware releases (e.g. NINA 2.2.0+, ESP32‑S3 3.0.1) the default is to require a CA or a pinned cert.Thus, with no CA or pinning, the handshake will not silently succeed; it will reject the server certificate and return false. If you see a successful connect() after you set a CA, the handshake is happening in firmware; if it still fails, the firmware is rejecting the chain.
setTime(1697000000) for a certificate valid from 2023‑01‑01 to 2024‑01‑01). If the handshake still fails, it is likely a chain or hostname issue.client.connect("example.com", 443). If this succeeds, the chain and SNI are acceptable.IPAddress ip = WiFi.hostByName("example.com");) and then call client.connect(ip, 443). If this fails while the hostname version succeeded, the failure is a hostname/SNI mismatch (or the firmware is dropping the hostname).Capture the exact error string via client.getLastError() or by enabling Serial.println(client.getLastErrorString());. A CN/SAN mismatch will produce a SSL_ERROR_BAD_CERT_DOMAIN‑style message, while a time error will yield SSL_ERROR_CERT_EXPIRED or SSL_ERROR_CERT_NOT_YET_VALID.
Serial.println(WiFiNINA::firmwareVersion());. For WiFiS3: Serial.println(esp32s3_firmware_version());. Compare against the release notes for the TLS default.WiFiSSLClient.h and look for setCACert(), setInsecure(), and the connect() overload that accepts a hostname. The presence of a setVerificationMode(VERIFY_NONE) flag indicates that the library can skip verification.WiFiSSLClient::setHostName() before connect(), the firmware is performing hostname verification.In summary, without a CA or pinned cert the default for all three stacks is to fail closed. A successful handshake after adding a CA proves the firmware is validating the chain. The four‑step test above isolates hostname/SNI errors from clock‑based X.509 validity problems.
To tailor the recommendation precisely, please provide the exact connect() call you are using (hostname vs IP) and the library/firmware versions installed on your board.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.