Prerequisites
TLS Security Addon for Best HTTP/2 to verify server certificate and send the device’s X.509 certificate.
You can follow different tutorials and guides to set up the IoT Hub and generate the required device certificate:
- https://garethwestern.com/posts/2020/02/17/mqtt-to-azure-iot-hub/
- https://docs.microsoft.com/en-us/azure/iot-hub/tutorial-x509-scripts
- https://github.com/Azure/azure-iot-sdk-c/blob/main/tools/CACertificates/CACertificateOverview.md
Setup TLS Security Addon
- Create a .pfx file from your device .crt and .key file with the following openssl command:
openssl pkcs12 -export -out device.pfx -inkey device.key -in device.crt
Add Client Certificate
- To add the device certificate open the Certificate Manager Window and at the bottom of the window under Client Certificates click on
Add for Domain
. - Add the hostname of the IoT Hub endpoint (
xyz.azure-devices.net
) to theDomain
field - Click on the
Select Certificate
button, locate and select the device.pfx created in the first step. Click on theOk
button and enter the password.
If everything goes right, a new entry will appear under the Client Certificates. Every time Best MQTT connects using TLS and the server asks for a client certificate the plugin will send this certificate back to the server.
Add Client Certificate Dinamically
All MQTT client connecting to an MQTT broker must have a unique clientId. To achieve this, devices should be added to the IoT Hub and device certificates generated dinamically.
To add a certificate
static void TryAddDeviceCertificateToDatabase(string host, string pathToCertificate, string password)
{
var database = TLSSecurity.ClientCredentials;
// find out whether we already added the device's client certificate
var certs = database.FindByTargetDomain(host);
if (certs == null || certs.Count == 0)
{
var store = new BestHTTP.SecureProtocol.Org.BouncyCastle.Pkcs.Pkcs12Store(System.IO.File.OpenRead(pathToCertificate), password.ToCharArray());
foreach (string alias in store.Aliases)
{
var certificate = new BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Certificate((from cert in store.GetCertificateChain(alias) select new BestHTTP.Addons.TLSSecurity.Databases.ClientCredentials.BestHTTPTlsCertificate(cert.Certificate.CertificateStructure)).ToArray());
var privateKeyInfo = BestHTTP.SecureProtocol.Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(store.GetKey(alias).Key);
database.Add(host, new BestHTTP.Addons.TLSSecurity.Databases.ClientCredentials.ClientCredential { Certificate = certificate, KeyInfo = privateKeyInfo });
}
database.Save();
}
}
And it can be used in the TLSSecurity.OnSetupFinished
callback:
TLSSecurity.OnSetupFinished = () => {
TryAddDeviceCertificateToDatabase("xyz.azure-devices.net", "<path to the .pfx file>", "<password>");
};
TLSSecurity.Setup();
Setup Best MQTT
After succesfully setting up the TLS Security Addon, connecting with Best MQTT requires the following steps:
- Use
.WithTLS
to ensure the plguin tries to connect using TLS. - Use
.WithProtocolVersion(SupportedProtocolVersions.MQTT_3_1_1)
as Azure IoT Hub supports MQTT v3.1.1 only. - Use the device name/id as the clientId in the
OnConnectPacketBuilder
callback (.WithClientID(deviceId)
call in the code below). - Use the combination of the host and device id as the username in the
OnConnectPacketBuilder
callback (.WithUserName($"{client.Options.Host}/{deviceId}")
call in the code below).
using System;
using BestHTTP.Addons.TLSSecurity;
using BestMQTT;
using BestMQTT.Packets.Builders;
using UnityEngine;
public sealed class MQTT_AzureTest : MonoBehaviour
{
// name/id of the device
string deviceId = "device2";
string host = "xyz.azure-devices.net";
void Start()
{
TLSSecurity.Setup();
var options = new ConnectionOptionsBuilder()
.WithTCP(host, 8883)
.WithTLS()
.WithProtocolVersion(SupportedProtocolVersions.MQTT_3_1_1)
.Build();
var client = new MQTTClient(options);
client.OnStateChanged += (c, oldState, newState) => Debug.Log($"[{c.Options.Host}]: {oldState} => {newState}");
client.OnConnected += OnClientConnected;
client.BeginConnect(OnConnectPacketBuilder);
}
private ConnectPacketBuilder OnConnectPacketBuilder(MQTTClient client, ConnectPacketBuilder builder)
{
return builder
.WithClientID(deviceId)
.WithUserName($"{client.Options.Host}/{deviceId}");
}
private void OnClientConnected(MQTTClient client)
{
client.CreateSubscriptionBuilder($"devices/{deviceId}/#")
.WithMessageCallback(OnDeviceMessage)
.BeginSubscribe();
}
private void OnDeviceMessage(MQTTClient client, SubscriptionTopic topic, string topicName, ApplicationMessage message)
{
Debug.Log($"{topic}({topicName}): {System.Text.Encoding.UTF8.GetString(message.Payload.Data, message.Payload.Offset, message.Payload.Count)}");
}
}