Unable to connect test.mosquitto.org over port 8885 encrypted, authenticated

I am trying to connect to test.mosquitto.org over port 8885 with tls from Android HiveMQ library.
Username : rw, password: readwrite

MqttClientBuilder mqttClientBuilder = MqttClient.builder()
                .identifier(UUID.randomUUID().toString())
                .serverHost("test.mosquitto.org")
                .serverPort(8885);

I tried with this:
mqttClientBuilder.sslWithDefaultConfig();

as well as this:

mqttClientBuilder.sslConfig(
                        MqttClientSslConfig
                                .builder()
                                .trustManagerFactory(null)
                                .keyManagerFactory(null)
                                .build()
                );

I use this builder to create Mqtt3AsyncClient with this code:

Mqtt3AsyncClient mqtt3Client = mqttClientBuilder
                .useMqttVersion3()
                .buildAsync();

I am getting this error while making connection.

[RxComputationThreadPool-1] connect failed javax.net.ssl.SSLHandshakeException: No subjectAltNames on the certificate match
com.hivemq.client.mqtt.exceptions.ConnectionFailedException: javax.net.ssl.SSLHandshakeException: No subjectAltNames on the certificate match
Caused by: javax.net.ssl.SSLHandshakeException: No subjectAltNames on the certificate match
	at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363)
	at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
	at com.android.org.conscrypt.Java8EngineWrapper.unwrap(Java8EngineWrapper.java:237)
	at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:309)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1473)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1366)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1415)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:1012)
Caused by: java.security.cert.CertificateException: No subjectAltNames on the certificate match
	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:419)
	at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:366)
	at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:102)
	at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:106)
	at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:256)
	at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638)
	at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
	at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataHeap(ConscryptEngine.java:1115)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1087)
	... 27 more

I am not sure why its not working. I tried to connect on 8883, 8886 which works fine with this code.
According to this page on https://test.mosquitto.org, port 8885 does not require any certificate.

Above error is related to certificate.

Any help would be greatly appreciated. We want to use this lib on prod app. Thank you.

Hi Palakkumar,

Welcome to the community, and thanks for your question!

The error you’re encountering, 'No subjectAltNames on the certificate match', occurs because the MQTT client cannot validate the server’s certificate due to a mismatch or absence of Subject Alternative Names (SAN) in the certificate. Port 8885 on test.mosquitto.org requires a trusted certificate for TLS communication.

To resolve this issue, you can explicitly download and use the server certificate (or the CA certificate) from test.mosquitto.org for secure communication. Here’s how you can fetch it:

openssl s_client -showcerts -connect test.mosquitto.org:8885 </dev/null 2>/dev/null | openssl x509 -outform PEM > mosquitto_cert.pem

Once you have the certificate, put it into a truststore and configure your MQTT client to use the trust store.

Hope this helps,
Best,
Dasha from The HiveMQ Team

Thanks for your reply Daria_H.

I am bit unsure about client certificate for port 8885.
I tried connecting via MQTT Explorer without any certificate and connection worked successfully.

Moreover, our iOS implementation does not involve any certification work still able to connect.

Our previous code with Eclipse Paho client did not have any certification at all.

In here, connecting to 8885 does not require certificate as its mentioned for port 8884.

Best regards,
Palak

Hi Palakkumar,

Thank you for your detailed follow-up.

The error message 'No subjectAltNames on the certificate match' indicates that the client’s SSL/TLS implementation is performing strict hostname verification against the server’s certificate. This means that the client expects the server’s certificate to include a Subject Alternative Name (SAN) that matches the server’s hostname (test.mosquitto.org). If the certificate lacks this SAN entry, or if it doesn’t match the expected hostname, the client will reject the connection to prevent potential security risks.

In your case, while other clients like MQTT Explorer and your iOS implementation can connect without specifying a certificate, they might be configured to either skip hostname verification or handle it differently. The HiveMQ client library, however, enforces strict hostname verification by default to ensure secure connections.

To address this issue, you have a couple of options:

  1. Disable Hostname Verification (Not Recommended for Production):

    You can configure the HiveMQ client to disable hostname verification. This approach can be useful for testing purposes but is not recommended for production environments due to security implications.

    Here’s how you can do it:

    MqttClientSslConfig sslConfig = MqttClientSslConfig.builder()
        .hostnameVerifier((hostname, session) -> true)
        .build();
    
    MqttClientBuilder mqttClientBuilder = MqttClient.builder()
        .identifier(UUID.randomUUID().toString())
        .serverHost("test.mosquitto.org")
        .serverPort(8885)
        .sslConfig(sslConfig);
    

    This configuration bypasses hostname verification by always returning true in the hostnameVerifier.

  2. Use a Custom TrustManager:

    If you have access to the server’s certificate or the Certificate Authority (CA) that issued it, you can create a custom TrustManager that trusts this specific certificate. This approach maintains a level of security by ensuring that you’re connecting to a trusted server, even if hostname verification is bypassed.

    Here’s an example:

    // Load your server's certificate
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = new FileInputStream("path/to/your/server_certificate.crt");
    Certificate ca = cf.generateCertificate(caInput);
    
    // Create a KeyStore containing the trusted certificate
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);
    
    // Create a TrustManager that trusts the certificate in our KeyStore
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    
    // Create an SSLContext that uses our TrustManager
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, tmf.getTrustManagers(), null);
    
    MqttClientSslConfig sslConfig = MqttClientSslConfig.builder()
        .sslContext(sslContext)
        .build();
    
    MqttClientBuilder mqttClientBuilder = MqttClient.builder()
        .identifier(UUID.randomUUID().toString())
        .serverHost("test.mosquitto.org")
        .serverPort(8885)
        .sslConfig(sslConfig);
    

    Replace "path/to/your/server_certificate.crt" with the actual path to the server’s certificate file.

Important Consideration:

While these approaches can help you establish a connection, it’s crucial to understand the security implications. Disabling hostname verification or trusting specific certificates can expose your application to man-in-the-middle attacks if not handled carefully. Always ensure that you’re implementing security best practices, especially in production environments.

If possible, consider reaching out to the administrators of test.mosquitto.org to inquire about the certificate’s SAN entries. They might provide a certificate with the appropriate SANs, allowing for standard hostname verification.

I hope this helps you resolve the connection issue. If you have further questions or need additional assistance, feel free to ask.

Best regards,

Dasha from The HiveMQ Team