SSLClient icon indicating copy to clipboard operation
SSLClient copied to clipboard

Expected server name was not found in the chain

Open jjauzion opened this issue 3 years ago • 6 comments

I'm trying to setup mutual TLS on Arduino Nano 33 IoT for a MQTT connection (using this library for MQTT along with SSLClient) but I have this error:

(SSLClient)(SSL_ERROR)(available): Cannot operate on a closed SSL connection.
(SSLClient)(SSL_ERROR)(m_print_br_error): Expected server name was not found in the chain.

Here is how I implement the connection in the arduino code:

WiFiClient wifiClient;
SSLClientParameters mTLS = SSLClientParameters::fromPEM(my_cert, sizeof(my_cert), my_key, sizeof(my_key));
SSLClient my_client(wifiClient, TAs, (size_t)TAs_NUM, A2);
MqttClient mqttClient(my_client);

[...]

// set client session
mqttClient.setCleanSession(true);
mqttClient.setId("mcu_sensor_client" + String(millis()));
mqttClient.setKeepAliveInterval(60 * 1000L);
mqttClient.setUsernamePassword(mqttUser, mqttPassword);

// set lastWill message
strcpy(payload, STATUS_KO);
retain = true;
qos = QOS_STATUS;
mqttClient.beginWill(mcu_status_topic, strlen(payload), retain, qos);
mqttClient.print(payload);
mqttClient.endWill();

// try to connect
if (!mqttClient.connect(broker, port)) {
  Serial.print("MQTT connection failed! Error code = ");
  Serial.println(mqttClient.connectError());
  return mqttClient.connectError();
}
digitalWrite(LED_BUILTIN, LOW);
Serial.println("You're connected to the MQTT broker!");

I use a self signed certificate ca.crt and the mcu cert files mcu_sensor.pem and mcu_sensor.key. I check my certificate with :

  • openssl verify -CAfile ca.crt mcu_sensor.pem; output: mcu_sensor.pem: OK
  • with docker run -v {pwd}/mcu_sensor.pem:/certs/cert.pem -v {pwd}/mcu_sensor.key:/certs/key.pem -v {pwd}/ca.crt:/certs/cacerts.pem superseb/cert-check:latest 192.168.0.14 (as explained in this article pointed out by the doc) which output:
INFO: Found CN localhost
INFO: Found Subject Alternative Name(s) (SANs): 192.168.0.14 host.docker.internal localhost node1.emqx.io
OK: 192.168.0.14 was found in SANs (192.168.0.14 host.docker.internal localhost node1.emqx.io)
OK: Certificate and certificate key match
OK: Certificate chain is complete
INFO: Showing certificate chain from /certs/cert.pem
subject=/C=FR/ST=IleDeFrance/L=Paris/O=myCompany/CN=localhost
issuer=/C=FR/ST=IleDeFrance/L=Paris/O=myCompany/CN=local host

I really don't understand why it says server name not found ? Everything looks ok with my certificates

PS: I can share the certificates if it helps (I can re generate new ones after ;)

jjauzion avatar Jun 23 '21 17:06 jjauzion

Hello! That sounds like a problem with your trust anchors (otherwise known as the certificates.h file). You can generate the correct trust anchor using the pycert_bearssl tool by running:

pycert_bearssl.py convert --no-search ca.crt

Try replacing your current certificates.h file with the one generated by the above command and see if that fixes it.

If that doesn't fix it, verify that the common name (CN) of the server certificate matches the domain name you're connecting to (broker in this case), as the connection won't be valid if it isn't. This verification prevents malicious actors from stealing a certificate from www.google.com and using it for their own malicious site (www.badgoogle.com), as the certificate must match the domain. You can test this issue by using a raw IP address instead of a domain in broker, and if the connection succeeds it means you should re-create your server certificate with the domain name you are connecting to.

prototypicalpro avatar Jun 23 '21 18:06 prototypicalpro

I did used pycert_bearssl.py to generate my certificates.h

My server has no domain name, it's only running in a local network. I tried to change the CN to the IP of the broker in the broker.pem (certifacte used by the broker), did not worked; I tried also to change the CN of the ca.crt file (the custom Certificate Authority) but still not working. (The IP was already defined in the Subject Alternative Name of the broker.pem)

Those certificates are working just fine using with other MQTT client (one written in Go with [pahoMqtt lib](https://github.com/eclipse/paho.mqtt.golang/tree/master/cmd/stdinpub and the other is telegraf)

As the certificates are working in other clients, my guess would be that something is wrong in the certificates.h, either because I did something wrong when using pycert_bearssl or maybe because of a glitch in the pycert_bearssl code ?

Here are some info on my certificates by running openssl x509 -in mcu_sensor.pem -text

  • ca.crt (certificate authority)
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            [...]
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = 192.168.0.14
        Validity
            Not Before: Jun 24 08:24:34 2021 GMT
            Not After : Jun 22 08:24:34 2031 GMT
        Subject: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = 192.168.0.14
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    [...]
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                [...]
            X509v3 Authority Key Identifier:
                keyid:[...]

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         [...]
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
  • mcu_sensors.pem (certificate used by the arduino via the certificates.h)
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            [...]
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = 192.168.0.14
        Validity
            Not Before: Jun 24 08:24:35 2021 GMT
            Not After : Jun 22 08:24:35 2031 GMT
        Subject: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    [...]
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                IP Address:192.168.0.14, DNS:localhost, DNS:node1.emqx.io, DNS:host.docker.internal
    Signature Algorithm: sha256WithRSAEncryption
         [...]
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
  • broker.pem (certificate used by the broker)
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            [...]
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = 192.168.0.14
        Validity
            Not Before: Jun 24 09:38:38 2021 GMT
            Not After : Jun 22 09:38:38 2031 GMT
        Subject: C = FR, ST = IleDeFrance, L = Paris, O = myCompany, CN = 192.168.0.14
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    [...]
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                IP Address:192.168.0.14, DNS:localhost, DNS:node1.emqx.io, DNS:host.docker.internal
    Signature Algorithm: sha256WithRSAEncryption
         [...]
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----

jjauzion avatar Jun 24 '21 09:06 jjauzion

Which .pem file are you using to generate the certificates.h? If I'm understanding correctly mcu_sensor.pem/key is the client certificate, broker.pem/key is the server certificate and ca.crt is the root for them both: given these assumptions, mcu_sensor.pem/key should be in my_cert/my_key, broker.pem/key should be in the MQTT server config, and ca.crt should be in both the server config and in certificates.h. Is this how you have it setup?

The other issue that could be occurring is with the CN of the server certificate being the same as the CN of the CA. I think strictly speaking it's a valid certificate chain, but I vaguely remember BearSSL not liking that kind of configuration: you might try changing the CN of your CA to something else (it can be anything you want, but I wouldn't use a domain or IP address) and regenerating the server/client certificates. The CN of your client and server certificates look good (assuming you're connecting to 192.168.0.14 in the code).

prototypicalpro avatar Jun 30 '21 23:06 prototypicalpro

Thanks for helping, sry for the late reply.

If I'm understanding correctly mcu_sensor.pem/key is the client certificate, broker.pem/key is the server certificate and ca.crt is the root for them both: given these assumptions, mcu_sensor.pem/key should be in my_cert/my_key, broker.pem/key should be in the MQTT server config, and ca.crt should be in both the server config and in certificates.h. Is this how you have it setup?

Yes! that's it

The other issue that could be occurring is with the CN of the server certificate being the same as the CN of the CA

I tried, I generated new certificate where CN is different for the CA and the server but I still have the same message.

I changed the MQTT librairy I was using because I had some other issue. With this new library I don't have the same error, the connection just fails (error coming from the mqtt Client: NETWORK_FAILED_CONNECT)

Anyway for the time being I'm not using SSL and will come back on this when I have more time :)

jjauzion avatar Jul 13 '21 14:07 jjauzion

I am having the same issue. I created my own CA certificate ca.crt and signed my server certificate server.crt with it via csr ( I followed this guide). The server.crt has the local IP as CN and the same IP plus the hostname as SAN entries. The CN for ca.crt is different from that (just my name). When I add ca.crt to my browsers trusted authorities and visit the IP, everything is fine and works. When I use pycert_bearssl.py convert --no-search ca.crt to create the header I see no error. But when running the code, I get

(SSLClient)(SSL_WARN)(m_run_until): Terminating because the ssl engine closed
(SSLClient)(SSL_ERROR)(m_start_ssl): Failed to initlalize the SSL layer
(SSLClient)(SSL_ERROR)(m_print_br_error): Expected server name was not found in the chain.

Is it possible to output the expected server name somehow to narrow down the mismatch?

Gretel5X avatar Feb 04 '22 12:02 Gretel5X

I believe there is something wrong when handling the SSL/TLS connection. What works for me is that SAN information must be included when creating the server CSR, exclude when creating CRT.

This however is in conlict with most browser. E.g. Edge you need to include SAN when creating CRT. Could be a bug in openssl when creating CSR and CRT.

lkjell avatar May 23 '23 08:05 lkjell