php-mqtt/client

Working with TSL

malja opened this issue · 4 comments

malja commented

Hi guys, I am trying to connect to the MQTT broker using a self-signed certificate. My code is as follows:

$connectionSettings = (new ConnectionSettings())
            ->setUsername($username)
            ->setPassword($password)
            ->setUseTls(true)
            ->setTlsCertificateAuthorityFile("{$path_to_certs}/ca.pem")
            ->setTlsClientCertificateFile("{$path_to_certs}/client_public.pem")
            ->setTlsClientCertificateKeyFile("{$path_to_certs}/client_private.pem")
            ->setTlsSelfSignedAllowed(true);

        $client = new MqttClient($host, $port], "client", MqttClient::MQTT_3_1, null, $logger);
        $client->connect($connectionSettings, true);

When I run this code, I am getting the following error: Establishing a connection to the MQTT broker failed: TLS error [1416F086]: SSL routines:tls_process_server_certificate:certificate verify failed.

I am able to connect to the broker with MQTT Explorer App using the same credentials. So there should be no problem with the TLS certificates or username/password.

So I tried connecting without using TLS. I don't know what the process behind connecting to a broker that requires TLS for the connection without TLS enabled on the client is. However, I am getting into an infinite loop inside the connect method.

I dug deeper, and it seems, that it loops in readFromSocket function:

while (feof($this->socket) === false && $remaining > 0) {

    $receivedData = fread($this->socket, $remaining);

    if ($receivedData === false) {

        $this->logger->error('Reading data from the socket of the broker failed.');
        throw new DataTransferException(
            DataTransferException::EXCEPTION_RX_DATA,
            'Reading data from the socket failed. Has it been closed?'
        );
    }
    $result   .= $receivedData;
    $remaining = $limit - strlen($result);
}

For some reason, $receivedData == false (but the condition $receivedData === false is false) and because $remaining keeps at value 1, it never leaves the loop.

Questions

So my question is twofold:

  1. How can I debug the connection with TLS enabled to see where the problem is?
  2. Is the described behavior when the TLS is disabled expected?

Notes:

  • url is in the form domain.subdomain.com
  • port is set to 8883

The error basically says that your local PHP script rejected the server certificate. The reasons for this can be manifold, but here are some initial ideas for you to check:

  • The server certificate may be invalid according to the clock of your client system. This is rather uncommon for network devices as they should have a network synchronized clock, but can be a thing in IoT environments.

  • The server certificate was not issued for the domain you connect to (happens frequently when connecting via IP address). There are additional settings which allow you to disable this verification step:

     // This flag determines if the peer certificate is verified, if TLS is used.
    ->setTlsVerifyPeer(true)
    // This flag determines if the peer name is verified, if TLS is used.
    ->setTlsVerifyPeerName(true)

Regarding your connection attempt to the TLS port without TLS enabled:
This cannot possibly work and it is also hard to tell whether the behavior is to be expected or not, as this might depend on the TLS version and other things. To be fair, I've not tested this and since it is not a use case, I also won't in the future. 👍


By the way, I assume the bracket after $port in

$client = new MqttClient($host, $port], "client", MqttClient::MQTT_3_1, null, $logger);

is an error only present in your post, because it shouldn't run at all in this form.

malja commented

By the way, I assume the bracket after $port in ... is an error only present in your post, because it shouldn't run at all in this form.

You are right. The bracket is just leftover from the original variable, which is actually an array.

The server certificate may be invalid according to the clock of your client system. This is rather uncommon for network devices as they should have a network synchronized clock, but can be a thing in IoT environments.

Again, you are right. The CA is expired. However it does not bother other tools and languages (python, javascript). Is this something that only PHP checks? Is there a way to disable the check?

The server certificate was not issued for the domain you connect to (happens frequently when connecting via IP address). There are additional settings which allow you to disable this verification step:

I have tried those, but since the problem is with the expired CA certificate (I guess), it did not help.

Again, you are right. The CA is expired. However it does not bother other tools and languages (python, javascript). Is this something that only PHP checks? Is there a way to disable the check?

Hm, that's quite odd. The validity is one of the most important things on a certificate, so most applications should definitely check this. I'm kinda surprised this is not the case. Maybe these applications disable the validity verification when you allow self-signed certificates? No idea to be honest.

However, I'm not aware of a way to disable this check in PHP. So the only way to fix this seems to issue a new CA...

malja commented

Yeah, I kind of expected that... 🥲

Thank you for your time and quick response. 👌 I'll get the new CA certificate.