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;
}