X.509 Certificate Based Authentication

X.509 Certificate Based Authentication is used in Two-Way SSL connection. In this case, the certificate itself is the client’s ID, thus, Access Token is no longer needed.

Instructions below will describe how to generate a client-side certificate and connect to the server that is running MQTT over SSL. You will need to have the public key of the server certificate in PEM format. See following instructions for more details on server-side configuration.

Update keygen.properties file

Open the keygen.properties file, and update the values if needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DOMAIN_SUFFIX="$(hostname)"
ORGANIZATIONAL_UNIT=NDU
ORGANIZATION=NDU
CITY=San Francisco
STATE_OR_PROVINCE=CA
TWO_LETTER_COUNTRY_CODE=US

SERVER_KEYSTORE_PASSWORD=server_ks_password
SERVER_KEY_PASSWORD=server_key_password

SERVER_KEY_ALIAS="serveralias"
SERVER_FILE_PREFIX="mqttserver"
SERVER_KEYSTORE_DIR="/etc/thingsboard/conf/"

CLIENT_KEYSTORE_PASSWORD=password
CLIENT_KEY_PASSWORD=password

CLIENT_KEY_ALIAS="clientalias"
CLIENT_FILE_PREFIX="mqttclient"

Run Client keygen script

Download and launch the client.keygen.sh script.

1
2
chmod +x client.keygen.sh
./client.keygen.sh

The script outputs the following files:

Provision Client Public Key as Device Credentials

Go to NDU Web UI -> Devices -> Your Device -> Device Credentials. Select X.509 Certificate device credentials, insert the contents of CLIENT_FILE_PREFIX.pub.pem file and click save. Alternatively, the same can be done through the REST API.

Run Two-Way MQTT SSL Python Client

Download Python client example two-way-ssl-mqtt-client.py. Specify your client-side certificate and path to the public key of the server certificate.

1
2
3
4
5
6
# Some code omitted

client.tls_set(ca_certs="mqttserver.pub.pem", certfile="mqttclient.nopass.pem", keyfile=None, cert_reqs=ssl.CERT_REQUIRED,
                       tls_version=ssl.PROTOCOL_TLSv1, ciphers=None);

# Some code omitted

Note Script uses 8883 MQTT port and requires paho-mqtt library that you can install using the following command: pip install paho-mqtt

Run the script:

If everything was configured correctly, the output should be like:

resources/mqtt-ssl-configuration-twowaysslmqttclient-output.txt
1
2
3
Connected with result code 0
Topic: v1/devices/me/attributes/response/1
Message: {}

To run Java client, import CLIENT_FILE_PREFIX.jks file as follows:

resources/MqttSslClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import com.google.common.io.Resources;
import org.eclipse.paho.client.mqttv3.*;

import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;

public class MqttSslClient {

    private static final String MQTT_URL = "ssl://localhost:8883";

    private static final String clientId = "MQTT_SSL_JAVA_CLIENT";
    private static final String keyStoreFile = "mqttclient.jks";
    private static final String JKS="JKS";
    private static final String TLS="TLS";
    private static final String CLIENT_KEYSTORE_PASSWORD = "password";
    private static final String CLIENT_KEY_PASSWORD = "password";

    public static void main(String[] args) {

        try {

            URL ksUrl = Resources.getResource(keyStoreFile);
            File ksFile = new File(ksUrl.toURI());
            URL tsUrl = Resources.getResource(keyStoreFile);
            File tsFile = new File(tsUrl.toURI());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            KeyStore trustStore = KeyStore.getInstance(JKS);
            trustStore.load(new FileInputStream(tsFile), CLIENT_KEYSTORE_PASSWORD.toCharArray());
            tmf.init(trustStore);
            KeyStore ks = KeyStore.getInstance(JKS);

            ks.load(new FileInputStream(ksFile), CLIENT_KEYSTORE_PASSWORD.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, CLIENT_KEY_PASSWORD.toCharArray());

            KeyManager[] km = kmf.getKeyManagers();
            TrustManager[] tm = tmf.getTrustManagers();
            SSLContext sslContext = SSLContext.getInstance(TLS);
            sslContext.init(km, tm, null);

            MqttConnectOptions options = new MqttConnectOptions();
            options.setSocketFactory(sslContext.getSocketFactory());
            MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
            client.connect(options);
            Thread.sleep(3000);
            MqttMessage message = new MqttMessage();
            message.setPayload("{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4}".getBytes());
            client.publish("v1/devices/me/telemetry", message);
            client.disconnect();
            System.out.println("Disconnected");
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}