AWS IoT provides secure, bi-directional communication between Internet-connected devices such as sensors, actuators, embedded micro-controllers, or smart appliances and the AWS Cloud. This enables you to collect telemetry data from multiple devices, and store and analyze the data. You can also create applications that enable your users to control these devices from their phones or tablets.
Provides a secure mechanism for devices and AWS IoT applications to publish and receive messages from each other. You can use either the MQTT protocol directly or MQTT over WebSocket to publish and subscribe.
The AWS IoT message broker is a publish/subscribe broker service that enables the sending and receiving of messages to and from AWS IoT. When communicating with AWS IoT, a client sends a message addressed to a topic like Sensor/temp/room1.
The message broker, in turn, sends the message to all clients that have registered to receive messages for that topic. The act of sending the message is referred to as publishing. The act of registering to receive messages for a topic filter is referred to as subscribing.
The topic namespace is isolated for each AWS account and region pair. For example, the Sensor/temp/room1 topic for an AWS account is independent from the Sensor/temp/room1 topic for another AWS account. This is true of regions, too. The Sensor/temp/room1 topic in the same AWS account in us-east-1 is independent from the same topic in us-east-2. AWS IoT does not support sending and receiving messages across AWS accounts and regions.
The message broker maintains a list of all client sessions and the subscriptions for each session. When a message is published on a topic, the broker checks for sessions with subscriptions that map to the topic. The broker then forwards the publish message to all sessions that have a currently connected client.
TsgcIoTAmazon_MQTT_Client is the component used for connect to AWS IoT, one client can connect to only one device. Client connects using plain MQTT protocol and authenticates using a X.509 Client Certificate.
In order to connect to AWS IoT, client needs the following properties:
Amazon.ClientId: identification of client, optional.
Amazon.Endpoint: server name where MQTT client will connect.
Amazon.Port: by default uses port 8883. If port is 443, uses ALPN automatically to connect (Requires custom Indy version).
AWS IoT Core supports devices and clients that use the MQTT and the MQTT over WebSocket Secure (WSS) protocols to publish and subscribe to messages. The following table lists the protocols that the AWS IoT device endpoints support and the authentication methods and ports they use.
Protocol | Authentication | Port | ALPN Protocol Name |
MQTT over WebSocket | Signature Version 4 | 443 | |
MQTT over WebSocket | Custom Authentication | 443 | |
MQTT | X.509 client certificate | 443 | x-amzn-mqtt-ca |
MQTT | X.509 client certificate | 8883 | |
MQTT | Custom Authentication | 443 | mqtt |
Requires to create certificates in your Amazon AWS console and set the path were are stored.
Using OpenSSL as IOHandler you must set the certificate in the following paths
Certificate.Enabled: set to True if you want use certificates.
Certificate.CertFile: path to X.509 client certificate.
Certificate.KeyFile: path to X.509 client key file.
Using SChannel as IOHandler, first convert the PEM Certificate + Key to a PFX certificate, requires openssl binaries:
openssl pkcs12 -inkey 884ccf73ff-private.pem.key -in 884ccf73ff-certificate.pem.crt -export -out 884ccf73ff-certificate.pfx
Then set the following paths (there is no need to set the keyfile because is already included in the certificate).
Certificate.Enabled: set to True if you want use certificates.
Certificate.CertFile: path to PFX certificate
Requires create an user in your Amazon AWS console and save the Access and Secret key which will be used to Sign the WebSocket request.
SignatureV4.Enabled: set to True if you want use this type of Authentication.
SignatureV4.Region: the region were is located your device (example: us-east-1).
SignatureV4.AccessKey: the access key created in your amazon console or get as temporary credential
SignatureV4.SecretKey: the secret key created in your amazon console or get as temporary credential
SignatureV4.SessionToken: (conditional) if you are using Temporary Security Credentials, set here the security token.
OpenSSL_Options: configuration of the openSSL libraries.
APIVersion: allows defining which OpenSSL API will be used.
oslAPI_1_0: uses API 1.0 OpenSSL, it's latest supported by Indy
oslAPI_1_1: uses API 1.1 OpenSSL, requires our custom Indy library and allows using OpenSSL 1.1.1 libraries (with TLS 1.3 support).
oslAPI_3_0: uses API 3.0 OpenSSL, requires our custom Indy library and allows using OpenSSL 3.0.0 libraries (with TLS 1.3 support).
LibPath: here you can configure where are located the openSSL libraries
oslpNone: this is the default, the openSSL libraries should be in the same folder where is the binary or in a known path.
oslpDefaultFolder: sets automatically the openSSL path where the libraries should be located for all IDE personalities.
oslpCustomFolder: if this is the option selected, define the full path in the property LibPathCustom.
LibPathCustom: when LibPath = oslpCustomFolder define here the full path where are located the openSSL libraries.
UnixSymLinks: enable or disable the loading of SymLinks under Unix systems (by default is enabled, except under OSX64):
oslsSymLinksDefault: by default are enabled except under OSX64 (after MacOS Monterey fails trying to load the library without version.).
oslsSymLinksLoadFirst: Load SymLinks and do before trying to load the version libraries.
oslsSymLinksLoad: Load SymLinks after trying to load the version libraries.
oslsSymLinksDontLoad: don't load the SymLinks.
*SignatureV4 requires Indy 10.5.7+
Custom authentication enables you to define how to authenticate and authorize clients by using authorizer resources. The device passes credentials in either the request’s header fields or query parameters (for MQTT over WebSockets protocols) or in the user name and password field of the MQTT CONNECT message (for the MQTT and MQTT over WebSockets protocols).
CustomAuthentication.Enabled: set to True if you want to use this type of Authentication.
CustomAuthentication.Parameters: set here the query parameters which will be passed to the server (by default is /mqtt)
CustomAuthentication.Headers: here you can put the custom header fields.
CustomAuthentication.WebSockets: if set to true, the connection will work over WebSocket protocol, otherwise will work over plain TCP.
MQTTAuthentication.Enabled: if you need to pass the username/password in the mqtt connection, enable this property
MQTTAuthentication.Username: username of the mqtt connection
MQTTAuthentication.Password: secret of the mqtt connection.
Client can send optionally a ClientId to identifiy client connection, then others clients can subscribe to receive a notification every time this client has connected, subscribed, disconnected...
If you can't connect using port 8883 and use TCP as transport (which is the default), amazon takes "AWS IoT Core policy" to provide or not authorization to clients and subscriptions. Most probably you must authorize your client id.
Enter in your Amazon AWS console, go to IoT Core and access the menu "Secure/Policies", there select the policy attached to your IoT Thing and check at the end how connection is configured. Example:
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:us-east-1:222178873557:client/sdk-java",
"arn:aws:iot:us-east-1:222178873557:client/basicPubSub",
"arn:aws:iot:us-east-1:222178873557:client/sdk-nodejs-*"
]
}
This configuration means that only clients with ID: sdk-java, basicPubSub and sdk-nodejs-* will be allowed to connect. Change accordingly and try again.
If it still doesn't work, enable log and check in cloudwatch the reason why you can't connect.
MQTTHeartBeat: if enabled attempts to keep alive MQTT connection sending a ping every x seconds.
Interval: number of seconds between each ping.
MQTTAuthentication: if enabled includes in MQTT connection the username and password
UserName: name of the user
Password: secret string
WatchDog: if enabled, when an unexpected disconnection is detected, tries to reconnect to the server automatically.
Interval: seconds before reconnection attempts.
Attempts: maximum number of reconnection attempts; zero means unlimited.
LogFile: if enabled, saves socket messages to a log file (useful for debugging). The access to log file is not thread safe if it's accessed from several threads.
Enabled: if enabled every time a message is received and sent by socket it will be saved on a file.
FileName: full path to the filename.
Amazon MQTT implementation is based on MQTT version 3.1.1 but it deviates from the specification as follows:
In AWS IoT, subscribing to a topic with Quality of Service (QoS) 0 means a message is delivered zero or more times. A message might be delivered more than once. Messages delivered more than once might be sent with a different packet ID. In these cases, the DUP flag is not set.
AWS IoT does not support publishing and subscribing with QoS 2. The AWS IoT message broker does not send a PUBACK or SUBACK when QoS 2 is requested.
When responding to a connection request, the message broker sends a CONNACK message. This message contains a flag to indicate if the connection is resuming a previous session. The value of this flag might be incorrect if two MQTT clients connect with the same client ID simultaneously.
When a client subscribes to a topic, there might be a delay between the time the message broker sends a SUBACK and the time the client starts receiving new matching messages.
The MQTT specification provides a provision for the publisher to request that the broker retain the last message sent to a topic and send it to all future topic subscribers. AWS IoT does not support retained messages. If a request is made to retain messages, the connection is disconnected.
The message broker uses the client ID to identify each client. The client ID is passed in from the client to the message broker as part of the MQTT payload. Two clients with the same client ID are not allowed to be connected concurrently to the message broker. When a client connects to the message broker using a client ID that another client is using, a CONNACK message is sent to both clients and the currently connected client is disconnected.
On rare occasions, the message broker might resend the same logical PUBLISH message with a different packet ID.
The message broker does not guarantee the order in which messages and ACK are received.
First, you must sign in your AWS console, register a new device and create a X.509 certificate for this device. Once is done, you can create create a new TsgcIoTAmazon_MQTT_Client and connect to AWS IoT Server. For example:
oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
oClient.Amazon.Endpoint := 'a2ohgdjqitsmij-ats.iot.us-west-2.amazonaws.com';
oClient.Amazon.ClientId := 'sgcWebSockets';
oClient.Certificate.CertFile := 'amazon-certificate.pem.crt';
oClient.Certificate.KeyFile := 'amazon-private.pem.key';
oClient.OnMQTTConnect := OnMQTTConnectEvent;
oClient.Active := True;
procedure OnMQTTConnect(Connection: TsgcWSConnection; const Session: Boolean; const ReturnCode: TmqttConnReturnCode);
begin
ShowMessage('Connected to AWS');
end;
The message broker uses topics to route messages from publishing clients to subscribing clients. The forward slash (/) is used to separate topic hierarchy. The following table lists the wildcards that can be used in the topic filter when you subscribe. # Must be the last character in the topic to which you are subscribing. Works as a wildcard by matching the current tree and all subtrees.
For example, a subscription to Sensor/# receives messages published to Sensor/, Sensor/temp, Sensor/temp/room1, but not the messages published to Sensor.
+ Matches exactly one item in the topic hierarchy. For example, a subscription to Sensor/+/room1 receives messages published to Sensor/temp/room1, Sensor/moisture/room1, and so on.
oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
...
oClient.OnSubscribe := OnSubscribeEvent;
vPacketIdentifier := oClient.Subscribe('Sensor/moisture/room1');
procedure OnMQTTSubscribe(Connection: TsgcWSConnection; aPacketIdentifier: Word; aCodes: TsgcWSSUBACKS);
begin
if vPacketIdentifier = aPacketIdentifier then
ShowMessage('Subscribed to topic Sensor/moisture/room1');
end;
// Client, can send a message using Publish method.
oClient.Publish('Sensor/moisture/room1', '{"temp"=10}');
// Messages received from server, are dispatched OnMQTTPublishEvent.
procedure OnMQTTPublish(Connection: TsgcWSConnection; aTopic, aText: string);
begin
DoLog('Received Message: ' + aTopic + ' ' + aText);
end;
Following methods are used to subscribe / publish to reserved topics.
Subscribe_ClientConnected(const aClientId: String): AWS IoT publishes to this topic when an MQTT client with the specified client ID connects to AWS IoT
Subscribe_ClientDisconnected(const aClientId: String): AWS IoT publishes to this topic when an MQTT client with the specified client ID disconnects to AWS IoT
Subscribe_ClientSubscribed(const aClientId: String): AWS IoT publishes to this topic when an MQTT client with the specified client ID subscribes to an MQTT topic
Subscribe_ClientUnSubscribed(const aClientId: String): AWS IoT publishes to this topic when an MQTT client with the specified client ID unsubscribes to an MQTT topic
Publish_Rule(const aRuleName, aText: String): A device or an application publishes to this topic to trigger rules directly
Publish_DeleteShadow(const aThingName, aText: String): A device or an application publishes to this topic to delete a shadow
Subscribe_DeleteShadow(const aThingName: String): A device or an application subscribe to this topic to delete a shadow
Subscribe_ShadowDeleted(const aThingName: String): The Device Shadow service sends messages to this topic when a shadow is deleted
Subscribe_ShadowRejected(const aThingName: String): The Device Shadow service sends messages to this topic when a request to delete a shadow is rejected
Publish_ShadowGet(const aThingName, aText: String): An application or a thing publishes an empty message to this topic to get a shadow
Subscribe_ShadowGet(const aThingName: String): An application or a thing subscribe to this topic to get a shadow
Subscribe_ShadowGetAccepted(const aThingName: String): The Device Shadow service sends messages to this topic when a request for a shadow is made successfully
Subscribe_ShadowGetRejected(const aThingName: String): The Device Shadow service sends messages to this topic when a request for a shadow is rejected
Publish_ShadowUpdate(const aThingName, aText: String): A thing or application publishes to this topic to update a shadow
Subscribe_ShadowUpdateAccepted(const aThingName: String): The Device Shadow service sends messages to this topic when an update is successfully made to a shadow
Subscribe_ShadowUpdateRejected(const aThingName: String): The Device Shadow service sends messages to this topic when an update to a shadow is rejected
Subscribe_ShadowUpdateDelta(const aThingName: String): The Device Shadow service sends messages to this topic when a difference is detected between the reported and desired sections of a shadow
Subscribe_ShadowUpdateDocuments(const aThingName: String): AWS IoT publishes a state document to this topic whenever an update to the shadow is successfully performed
A persistent session represents an ongoing connection to an MQTT message broker. When a client connects to the AWS IoT message broker using a persistent session, the message broker saves all subscriptions the client makes during the connection. When the client disconnects, the message broker stores unacknowledged QoS 1 messages and new QoS 1 messages published to topics to which the client is subscribed. When the client reconnects to the persistent session, all subscriptions are reinstated and all stored messages are sent to the client at a maximum rate of 10 messages per second.
You create an MQTT persistent session setting the cleanSession parameter to False OnMQTTBeforeConnect event. If no session exists for the client, a new persistent session is created. If a session already exists for the client, it is resumed.
Devices need to look at the Session attribute in the OnMQTTConnect event to determine if a persistent session is present. If Session is True, a persistent session is present and stored messages are delivered to the client. If Session is False, no persistent session is present and the client must re-subscribe to its topic filters.
Persistent sessions have a default expiry period of 1 hour. The expiry period begins when the message broker detects that a client disconnects (MQTT disconnect or timeout). The persistent session expiry period can be increased through the standard limit increase process. If a client has not resumed its session within the expiry period, the session is terminated and any associated stored messages are discarded. The expiry period is approximate, sessions might be persisted for up to 30 minutes longer (but not less) than the configured duration.
AWS IoT Core can work with Temporary Credentials obtained through Identity Pools, there are 2 types of Identities:
If you are using Unauthenticated credentials, just attach the policy in the UnAuthenticated Role automatically created in the IAM menu. Then configure the client setting the Access, Secret Key and Token returned by Cognito service.
Find below a code in .NET to get unauthenticated credentials
CognitoAWSCredentials credentials = new CognitoAWSCredentials(
"us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f", // Identity pool ID
Amazon.RegionEndpoint.USEast1 // Region
);
var identityPoolId = credentials.GetCredentialsAsync();
AmazonCognitoIdentityClient cognitoClient = new AmazonCognitoIdentityClient(
credentials, // the anonymous credentials
Amazon.RegionEndpoint.USEast1 // the Amazon Cognito region
);
GetIdRequest idRequest = new GetIdRequest();
idRequest.AccountId = "222178873557";
idRequest.IdentityPoolId = "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f";
GetIdResponse idResp = cognitoClient.GetId(idRequest);
string AccessKey = identityPoolId.Result.AccessKey;
string SecretKey = identityPoolId.Result.SecretKey;
string SessionToken = identityPoolId.Result.Token;
string IdentityId = idResp.IdentityId;
Authenticated credentials, requires to attache de policy in the Authenticated Role automatically created in the IAM menu and attach the policy of the user in AWS IoT Core policies.
So create a new policy in the IoT Core policies menu and every time a new user authenticates, attach this policy to this user.
You can use the following command of AWS to attach a policy or create a lambda function.
aws iot attach-policy --policy-name PolicyName --target us-east-1:XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
The Fleet Provisioning service supports the following MQTT API operations:
CreateCertificateFromCsr
Use the method CreateCertificateFromCsr passing the CertificateSigningRequest as a parameter to create the certificate. In order to receive the response to this request, first subscribe to the following methods: SubscribeCreateCertificateFromCsrResponse and SubscribeCreateCertificateFromCsrError
CreateKeysAndCertificate
Use the method CreateKeysAndCertificate to create a new certificate and keys. In order to receive the response to this request, first subscribe to the following methods SubscribeCreateKeysAndCertificateResponse and SubscribeCreateKeysAndCertificateError
RegisterThing
Use the method RegisterThing to register a new thing passing as a parameter the Template Name and the Payload in JSON format. In order to receive the response to this request, first subscribe to the following methods SubscribeRegisterThingResponse and SubscribeRegisterThingError.