Postman can definitely connect to the HTTPs endpoint and publish a message, though the AWS IoT docs admittedly aren't the best on explaining this.
I want to make sure I answer all of your questions so there's a lot of detail here, but we'll go through it bit by bit.
Now i opened the AWS Iot test console and subscribe to a #
Correct.
#
and +
are the MQTT wildcard characters. Subscribing to either of them will subscribe you to all topics.
It's easy to confuse them with the AWS IAM policy wildcard characters - *
& ?
- which you can use in IoT Core policies but not for subscriptions. Using them as topic names for subscriptions won't yield any messages.
Which certificates do I use?
Your downloaded connection kit for Windows (as the names will differ for Linux/Mac) will have at a minimum:
{thing-name}.cert.pem
: device certificate
{thing-name}.private.key
: public key file
{thing-name}.public.key
: private key file
I'm using the eu-west-1
region which uses the above naming convention however, clearly it's not consistent across all regions.
Nonetheless, you can deduce it most of the time as the device certificate is the .pem
file, the private key has private in the name & the public key has public in the name.
In your case, they are respectively:
certificate.cert.pem
: device's certificate
public.pem.key
: device's public key file
private.pem.key
: device's private key file
You then may also have 1 or more CA (certificate authority) certificate files. Currently, per docs, AWS IoT Core server authentication certificates are signed by one of the following root CA certificates (but you might also see CA 2 and 4 :
- RSA 2048 bit key: Amazon Root CA 1
- ECC 256 bit key: Amazon Root CA 3
In your case, you have both Root CA 1 & Root CA 3 with names that conveniently point out the cipher suite:
Cipher suites are outside of the scope of this answer but we'll stick to RSA 2048.
So, what do we need for Postman?
We need:
- 1 certificate authority:
RSA2048AmazonRootCA1.pem
- device's certificate:
certificate.cert.pem
- device's private key:
private.pem.key
This is how they map in Postman.
Settings > Certificates > CA Certificates
- Toggle to ON
- Select your root CA (any would work with Postman, but other tools may not always support ECC)
- Add Certificate
- We have a few values here:
https://
, set by Postman
- your AWS IoT
Data-ATS
endpoint (aws iot describe-endpoint --endpoint-type iot:Data-ATS
), set by you e.g. xxx-ats.iot.eu-west-1.amazonaws.com
- 8443, set by you, as the port - do not use 443
- CRT file: select your device's certificate.
Postman is using the .crt
filename convention but that's all it is - a filename convention. The format of .crt
files are either in DER or PEM. AWS has used .pem
here to signal the PEM format i.e. Base64 encoding of the certificate.
Postman fully supports .pem
but doesn't currently support DER, so we can just keep this .pem
file as it is.
Side note: feel free to add .crt
on to the end to have Windows recognise it as a certificate file & allow you to double click on it to check details.
- Click Add - this should be your final configuration
Postman is now correctly configured to send requests to the HTTPS endpoint.
The reason IoT throws Missing authentication
when trying to publish messages via the HTTPS endpoint is that port 443 requires a custom ALPN protocol name of x-amzn-http-ca
. You can only communicate over port 443 without a custom ALPN protocol name set, if you use AWS Sig V4 for authentication.
In this case, as we're using X.509 client certificate authentication, port 8443 allows us to not need to configure ALPN. ALPN is impossible to configure within Postman & difficult to configure for curl, so we should opt for either
- Port 443, AWS Signature v4 authentication
- Port 8443, X.509 certificate authentication
I've opted for certificate authentication.
This is highlighted in the AWS docs, with my emphasis on the bold items:
Protocol |
Operations Supported |
Authentication |
Port |
ALPN Protocol Name |
MQTT over WebSocket |
Publish, Subscribe |
Signature Version 4 |
443 |
N/A |
MQTT over WebSocket |
Publish, Subscribe |
Custom authentication |
443 |
N/A |
MQTT |
Publish, Subscribe |
X.509 client certificate |
443 |
x-amzn-mqtt-ca |
MQTT |
Publish, Subscribe |
X.509 client certificate |
8883 |
N/A |
MQTT |
Publish, Subscribe |
Custom authentication |
443 |
mqtt |
HTTPS |
Publish only |
Signature Version 4 |
443 |
N/A |
HTTPS |
Publish only |
X.509 client certificate |
443 |
x-amzn-http-ca |
HTTPS |
Publish only |
X.509 client certificate |
8443 |
N/A |
HTTPS |
Publish only |
Custom authentication |
443 |
N/A |
Final thing to check is that the policies attached to your certificate allow you to publish to the topic you want to publish to, using that certificate.
At the very least, you need to have the below policy attached with the iot:Publish
policy action:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": "arn:aws:iot:eu-west-1:xxx:topic/my-topic"
}
]
}
Replace eu-west-1
with your region, xxx
with your AWS account ID & my-topic
with your topic name.
If you want to be able to publish to any topic, use the *
wildcard in the ARN as detailed before (and not #
!) e.g.
arn:aws:iot:eu-west-1:xxx:topic/*
Once you have the correct CA, the correct device certificate set up with the host set to the ATS signed data endpoint, the port set to 8443, the KEY file set to your private key and your certifcate has the right policy attached in the console, you're ready to send your request.
- Method: POST
- URL:
https://xxx:8443/topics/yyy?qos=1
, replacing xxx
with your ATS endpoint and yyy
with your topic name
- Body: raw - Text/JSON as content type, doesn't really matter
- Headers: very important to keep
Content-Length
otherwise you will get Message cannot be displayed in specified format.
in the AWS MQTT test client
Hit send & a 200 OK
response signals success:
{
"message": "OK",
"traceId": "xxx-xxx-xxx-xxx-xxx"
}
If you haven't set up your policies correctly to allow publishing to the topic, you'll instead get a 403 Forbidden
response that signals failure.
Note message
is set to null
:
{
"message": null,
"traceId": "xxx-xxx-xxx-xxx-xxx"
}
If it's successful, check the console & your message will be there:
P.S. the developer guide does indeed have a typo with the rogue "
. I've submited feedback to hopefully rectify this :)