Heuristic Services     About     Archive     Feed

MQTT and SSL

Using a directly-trusted server certificate

Moquette is a broker that supports SSL. In the Moquette documentation, it mentions you can generate a keypair as a Java keystore:

~$ keytool -keystore serverkeystore.pkcs12 -alias testserver -genkey -keyalg RSA

Make sure that the path to the keystore is the filesystem path, not the file:/// URL.

final String keystorePath = Objects.requireNonNull(Broker.class.getResource("serverkeystore.pkcs12")).getPath();
properties.put(SSL_PORT_PROPERTY_NAME, "8883");
properties.put(JKS_PATH_PROPERTY_NAME, keystorePath);
properties.put(KEY_STORE_PASSWORD_PROPERTY_NAME, "password");
properties.put(KEY_MANAGER_PASSWORD_PROPERTY_NAME, "password");

If you use a client such as the hivemq-mqtt-client, then you will find that it fails, as it fails to validate the Subject Alternative Name (SAN).

Exception in thread "main" com.hivemq.client.mqtt.exceptions.ConnectionFailedException: javax.net.ssl.SSLHandshakeException: No subject alternative names present

A certificate can be generated using a SAN, in this case for localhost:

keytool -keystore serverkeystore.pkcs12 -alias testserver -genkey -keyalg RSA -dname "CN=127.0.0.1" -ext "SAN=IP:127.0.0.1"

You can list the certificate in the keystore with:

keytool -list -keystore serverkeystore.pkcs12

We’ll need to add this certificate to the client’s (in this case HiveMQTT) trust store, so export the certificate using:

keytool -export -alias testserver -keystore serverkeystore.pkcs12 -file testserver.crt

You can inspect the certificate by using openssql and specifying it needs to read the DER binary format:

openssl x509 -inform der -in testserver.crt -text

Continue to follow the Moquette documentation to create a keystore that trusts this certificate for the client:

keytool -keystore clientkeystore.pkcs12 -genkey -keyalg RSA
keytool -keystore clientkeystore.pkcs12 -import -alias testserver -file testserver.crt -trustcacerts

Configure the HiveMQTT client to use this directly trusted certificate and subscribe to a test message:

public static void main(String[] args) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, URISyntaxException, InterruptedException {
    final Mqtt3BlockingClient client = com.hivemq.client.mqtt.MqttClient.builder()
            .useMqttVersion3()
            .serverHost("127.0.0.1")
            .serverPort(8883)
            .identifier("testClient")
            .sslConfig().trustManagerFactory(getTrustManagerFactory()).applySslConfig()
            .buildBlocking();
    client.connect();

    client.toAsync()
            .subscribeWith()
            .topicFilter("+/topic/hello")
            .callback(e -> System.out.printf("%s %s", e.getTopic(), new String(e.getPayloadAsBytes())))
            .send();

    client.publishWith()
            .topic("test/topic/hello")
            .payload("Test message".getBytes(StandardCharsets.UTF_8))
            .send();
}

private static TrustManagerFactory getTrustManagerFactory() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, URISyntaxException {
    final KeyStore keystore = KeyStore.getInstance("pkcs12");
    final InputStream in = new FileInputStream(Objects.requireNonNull(Client.class.getResource("clientkeystore.pkcs12")).getFile());
    keystore.load(in, "password".toCharArray());

    final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keystore);

    return trustManagerFactory;
}