Connect MQTT broker with pre-signed url

Hi,

I’m having trouble with connecting mqtt broker with pre-signed url which looks like :
wss://…eu-central-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=…&X-Amz-Date=20211004T170341Z&X-Amz-SignedHeaders=host&X-Amz-Signature=…2e&X-Amz-Security-Token=…

val mqttClient: Mqtt5AsyncClient = com.hivemq.client.mqtt.mqtt5.Mqtt5Client.builder()
        .identifier(UUID.randomUUID().toString())
        .serverHost(url.host)
        .serverPort(443)
        .sslWithDefaultConfig()
        .webSocketConfig(
            MqttWebSocketConfig.builder()
                .serverPath("/mqtt")
                .queryString(url.query)
                .subprotocol("ws")
                .build()
        )
        .addConnectedListener {
            Timber.v("<------------------ connected !")
        }
        .addDisconnectedListener {
            Timber.v("<------------------ disconnected ! ${it.cause}")
        }
        .buildAsync()

        mqttClient.connectWith()
            .send()
            .whenComplete { connAct, throwable ->
                if (throwable != null) {
                } else {
                }
            }

This is my implementation, but keep throwing 403 forbidden error. When I test with MQTTExplorer, it connects successfully with the signed url. The screenshot shows how I set up the configuration

Anything I’m missing here? I couldn’t find the solution.

Hello Dooyong,

Thank you for your interest HiveMQ and trying our HiveMQ Client library! We are happy to help you with your use case. Could you please clarify, what exactly are you trying to achieve?

Note, that connecting a HiveMQ Client to AWS IoT Core not possible with MQTTv5 client since AWS IoT Core in not compatible with MQTTv5 specification yet.

  1. Are you using wss:// or ws://? I am asking because your screenshot and the snippet of your code do not match one another.
  2. What encryption and certificate are used? Are those the same on your screenshot and in your code?
  3. Which other information is encoded in your pre-signed URL, like host, port etc.?
  4. Is your serverPath /mqtt consistent with your Basepath mttt?X-AMz-...?

Please come back to us with missing info and we will be glad to help you with your use case.

Kind regards,
Daria from HiveMQ

Hi @Daria_H,

I also have this issue.
The url is the same of @Dooyoung and the error also is the same.
Do you any clue on what might be happening?

host = XXXXXXXX-ats.iot.eu-central-1.amazonaws.com
queryString = /mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXX

val client: Mqtt3BlockingClient = Mqtt3Client.builder()
    .addConnectedListener(MqttClientConnectedListener { context ->
    })
    .addDisconnectedListener(MqttClientDisconnectedListener { context ->
    })
    .identifier(udid)
    .serverHost(host)
    .serverPort(443)
    .webSocketConfig(mqttWebSocketConfig)
    .sslWithDefaultConfig()
    .buildBlocking()
val mqttWebSocketConfig = MqttWebSocketConfig.builder()
    .subprotocol("mqtt")
    .serverPath("/mqtt")
    .queryString(queryString)
    .build()
try {
     client.connect()
} catch (e: Exception) {}

Thanks,
Diogo,

Hi mestre,

glad to see your interest in MQTT. You have mentioned that you have an issue with your code, but did not paste the error message?

regards,
Dasha from HiveMQ Team

Hi @Daria_H ,

So I’ve tried the queryString in MqttWebSocketConfig with /mqtt? in the string and without it.
The results were not the same.

queryString=/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential…

MqttClientDisconnectedListener, message=Server closed connection without DISCONNECT.

queryString=X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential…

    com.hivemq.client.mqtt.exceptions.ConnectionFailedException: io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: Invalid handshake response getStatus: 403 Forbidden
    Caused by: io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: Invalid handshake response getStatus: 403 Forbidden
        at io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker13.verify(WebSocketClientHandshaker13.java:274)
        at io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker.finishHandshake(WebSocketClientHandshaker.java:302)
        at com.hivemq.client.internal.mqtt.handler.websocket.MqttWebsocketHandshakeHandler.finishHandshake(MqttWebsocketHandshakeHandler.java:103)
        at com.hivemq.client.internal.mqtt.handler.websocket.MqttWebsocketHandshakeHandler.channelRead(MqttWebsocketHandshakeHandler.java:94)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:295)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1486)
        at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1235)
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1282)
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498)
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:437)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)

Thanks,
Diogo.

Hi Diogo,

for the Secure WebSocket you should use “wss” instead of “tcp” or “mqtt”, and for insecure WebSocket you should use “ws”.

If you want us to check your code, you are of course welcome to post it. Please make sure that it is clear, which port you are using and how your password is encoded before it is being sent.

If you are suspecting that this is a HiveMQ cloud broker issue (which of course could happen), please also test your code with some other MQTT Broker having similar encryption and authorisation method. For example, with test.mosquitto.org:8885

Kind regards,
Dasha from HiveMQ team

Hi @Daria_H,

Thanks for the reply.
I’ve already shared my code, and I didn’t found any documentation that I can follow regarding a secure websocket connection with a pre-signed url from AWS.
So I have a url from AWS like this one:

wss://xxxxxxxxx-ats.iot.eu-central-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=XXXXXXXXXXXXX%2F20220218%2Feu-central-1%2Fiotdevicegateway%2Faws4_request
&X-Amz-Date=20220218T191211Z
&X-Amz-Expires=300
&X-Amz-SignedHeaders=host
&X-Amz-Signature=1909596d86a6b3e3ee51fb1c3fb0e16fbe5b3dff73a68d3022ee62256c9c548d
&X-Amz-Security-Token=dfgsfdgdfgsdDSFsdgsfg3dsv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDhrU3%2Fax0c

I split this url into the host and queryString like this:

host=xxxxxxxxx-ats.iot.eu-central-1.amazonaws.com
queryString=/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXXXXXXXXX%2F20220218%2Feu-central-1%2Fiotdevicegateway%2Faws4_request......

Then I create a websocket config and a mqtt3client:

val mqttWebSocketConfig = MqttWebSocketConfig.builder()
     .subprotocol("wss")
     .serverPath("mqtt")
     .queryString(queryString)
     .build()

val client: Mqtt3BlockingClient = Mqtt3Client.builder()
    .identifier("9146AC3BD50778C0DB5125D7B4BFC3F2")
    .serverHost("xxxxxxxxx-ats.iot.eu-central-1.amazonaws.com")
    .serverPort(443)
    .webSocketConfig(mqttWebSocketConfig)
    .buildBlocking()

client.connect()

So my questions are:

  1. What is supposed to put on the subprotocol? (I’ve tried “wss” and “mqtt”)
  2. The queryString is supposed to have the path? (I’ve tried with “/mqtt?” and without in the query)
  3. The server host is supposed to have the “wss://”?

I’m getting this error now after changing to subprotocol wss:

2022-02-18 19:17:05.862 3416-3972/com.witsoftware.vodafonetv E/IotSDK: MqttClientDisconnectedListener, message=io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: handshake timed out after 1000ms
2022-02-18 19:17:05.863 3416-3972/com.witsoftware.vodafonetv E/IotSDK: MqttClientDisconnectedListener
    com.hivemq.client.mqtt.exceptions.ConnectionFailedException: io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: handshake timed out after 1000ms
    Caused by: io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: handshake timed out after 1000ms
        at com.hivemq.client.internal.mqtt.handler.websocket.MqttWebsocketHandshakeHandler.lambda$startHandshake$0$MqttWebsocketHandshakeHandler(MqttWebsocketHandshakeHandler.java:81)
        at com.hivemq.client.internal.mqtt.handler.websocket.-$$Lambda$MqttWebsocketHandshakeHandler$wYOfs1pg0IXSdoZkmsItb8ZH5j8.run(Unknown Source:4)
        at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98)
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        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:920)

Thanks,
Diogo.

Hi mestre,

Are you already signed up for a free hivemq cloud account? Once you are signed, to go your cluster and click Manage Cluster → Getting started. In the list of sample client find one that says Java(Websocket) hivemq-mqtt-client:
image

There you will find an example of Websocket Client: https://console.hivemq.cloud/clients/websocket-java

Take a look at the code that is building the client:

final Mqtt5BlockingClient client = MqttClient.builder()
                .useMqttVersion5()
                .serverHost(host)
                .serverPort(8884)
                .sslWithDefaultConfig()
                .webSocketConfig()
                .serverPath("mqtt")
                .applyWebSocketConfig()
                .buildBlocking();

Note a few things:

  • subprotocol is not explicitly updated, it is, by default, “mqtt”;
  • serverPath is “mqtt” (like in your last attempt);
  • there is a call to .applyWebSocketConfig() before .buildBlocking();

Please try to change your code this way and let us know the result. For the queryString use the part of the string that follows the “?”: “protocol://host:port/mqtt?xxx-xxx-xxx-xxx-xxx-x…”

Thanks,
Dasha from HiveMQ Team

Hi,

I got it working by patching hivemq-mqtt-clint/src/main/java/com/hivemq/client/internal/mqtt/handler/websocket/MqttWebSocketInitializer.java.

Background: The signed websockets URL that should be used to connect to AWS IoT Core is already encoded. When the URI is generated for the websockets connection java.net.URI encodes the URI a second time and thus invalidates the signed websockets URL.

Therefore the second encoding must be reverted.

The following snippet made it working for me:

        final URI uri;
        final URI _uri;
        try {
            final MqttClientTransportConfigImpl transportConfig = clientConfig.getCurrentTransportConfig();
            final InetSocketAddress serverAddress = transportConfig.getServerAddress();
            _uri = new URI((transportConfig.getRawSslConfig() == null) ? "ws" : "wss", null,
                    serverAddress.getHostString(), serverAddress.getPort(), "/" + webSocketConfig.getServerPath(),
                    webSocketConfig.getQueryString(), null);

	    uri = new URI(URLDecoder.decode(_uri.toString(), StandardCharsets.UTF_8.toString()));
	    System.out.println("uri: " + uri);
        } catch (final URISyntaxException e) {
            onError.accept(channel, e);
            return;
        } catch (final UnsupportedEncodingException e) {
            onError.accept(channel, e);
            return;
	}

DISCLAIMER: I am not a developer. There might be better options to revert the second encoding or prevent it from the beginning.

Hello. Daria_H,

I am new to mqtt and developing a simple program using the HiveMQ client library. I am trying to connect to aws iot core with the endpoint provided by aws but I am getting this error message.

com.hivemq.client.mqtt.exceptions.ConnectionFailedException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Screen Shot 2022-05-02 at 10.51.53 AM

This is how i configure the client. I am seeking any advice and thank you all in advance.

Hi @chrisnoob ,

Great to see your interest in HiveMQ!
To help you I would need to reproduce your error message, please provide exact steps to do that. In case if you are following somebody else’s instructions, like in a how-to article or from the textbook, please give me the link.

Thanks,
Dasha from HiveMQ team

problem solved. turned out i need to include root certificate to trust store.

2 Likes

@Daria_H Hello, I got also an error by connecting to Amazon Iot Broker. Here the code example using a proxy server:

String BrokerUrl = "
wss://au1234m1uf12i-ats.iot.eu-west-1.amazonaws.com/mqtt?
X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=ASIA…
&X-Amz-Date=20240326T095507Z
&X-Amz-Expires=86400
&X-Amz-SignedHeaders=host&
X-Amz-Signature=9ea8d30…
&X-Amz-Security-Token=FwoGZXI…";
String clientId = “s-12cAbcDe12dFG1ysXmz8eE”

String proxyHost = “companyProxyHost”;
int proxyPort = 1234;

URI uri = new URI(brokerUrl);
MqttProxyConfig mqttProxyConfig = MqttProxyConfig.builder()
.address(new InetSocketAddress(proxyHost, proxyPort))
.build();
// Create MQTT client instance
String host = uri.getHost();
int port = 443;

Mqtt3ClientBuilder mqtt3ClientBuilder = Mqtt3Client.builder()
.serverHost(host)
.serverPort(port)
.identifier(clientId)
.sslWithDefaultConfig();

  .webSocketConfig(MqttWebSocketConfig.builder()
  		.serverPath("mqtt")
  		.queryString(uri.getQuery())
  		.subprotocol("wss")
  		.build())
  .transportConfig(MqttClientTransportConfig.builder()			
         .proxyConfig(mqttProxyConfig)
  		.serverHost(host) //target host
  		.serverPort(port) //target host
  		.sslWithDefaultConfig()
  		.build());

.automaticReconnectWithDefaultConfig();

// Define connection options
Mqtt3Connect connect = Mqtt3Connect.builder()
.cleanSession(true)
.build();

// Connect to the MQTT broker
Mqtt3BlockingClient mqtt3BlockingClient = mqtt3ClientBuilder.buildBlocking();
mqtt3BlockingClient.connect(connect);
log.info("Connected to MQTT broker. Connection state isConnected = " + mqtt3BlockingClient.getState().isConnected());

Result:

  1. with proxy config:

Caused by: io.netty.handler.proxy.ProxyConnectException: socks5, none, /“here the proxyHostIp”:3128 => au1234m1uf12i-ats.iot.eu-west-1.amazonaws…com/:443, timeout

2- without proxy config:

com.hivemq.client.mqtt.exceptions.ConnectionFailedException: io.netty.channel.ConnectTimeoutException: connection timed out: au1234m1uf12i-ats.iot.eu-west-1.amazonaws…com/“here the host ip”:443

Could you please support me by these errors and post a working example (with certificate if this is needed) on how to setup an MQTT connection to an amazon Broker with pre-signed url using proxy server?

@chrisnoob Could you please post a code example? That would be very helpful

Hello @Hiben,

Welcome to the HiveMQ Community! It’s great to see your enthusiasm for MQTT and the HiveMQ broker. We’re thrilled to have you onboard.

Regarding your query, I appreciate your interest in integrating AWS IoT Core MQTT broker with standard MQTT libraries. Indeed, according to AWS documentation Use AWS IoT Core MQTT broker with standard MQTT libraries, it’s advised to utilize a normal TLS-TCP connection on port 8883 instead of WebSocket or port 443.

If you have any further questions or need assistance, feel free to ask.

Best regards,
Dasha from HiveMQ Team

@Daria_H Thank you for quick reply. The Documentation of the broker I should connect to indicates that it uses Websockets protocol with port 443. That’s why I’m asking for an example on how to connect into it.
Thank you for support

@Hiben I understand your point. It would be helpful if you could share the link to the documentation you mentioned of how to connect to the broker using the WebSocket protocol on port 443. Thank you for your cooperation and support.

Best regards,
Dasha from HiveMQ Team

@Daria_H the documentation uses the libary Paho not hivemq. I diabled the proxy and now getting this error: com.hivemq.client.mqtt.exceptions.ConnectionFailedException: io.netty.handler.codec.http.websocketx.WebSocketClientHandshakeException: Invalid handshake response getStatus: 403 Forbidden

Like the previous posts. Could you please provide a running example for this case? The error appear n most of the posts since years but there is no concrete fix for that

Hello @Hiben,

Thank you for reaching out. It seems there might have been a small oversight in your message? If you intended to include a link to “The Documentation of the broker I should connect to indicates that it uses Websockets protocol with port 443,” please feel free to add it at your convenience.

Warm regards,

Dasha from HiveMQ Team

@Daria_H I could already connect to the Broker with using the 443 port and Paho Libary but this libary doesn’t have a proxy support like Hivemq. So I’m sure that it is the right port. I can not provide an internal documentation.
Again could you please provide support on how to connect to an amazon presigned url using hivemq and fix the Handshake exception mentioned in most of the posts?