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