Как сделать MQTT с TLS для IoT Hub через Ethernet (не WiFi)
Контекст
Я хочу выполнить публикацию D2C в Microsoft Azure IoT Hub с использованием MQTT через Ethernet (а не через WiFi).
Прошивка работает на моей специальной печатной плате на основе ESP32, к которой через SPI подключен модуль Ethernet (также известный как Wiz5500) (выбор микросхемы — GPIO_NUM_5).
Прошивка работает под управлением Arduino.
Центру Интернета вещей требуется, чтобы MQTT использовал определенный порт (он же 8883), а также использовал TLS (фактически mTLS).
Описание кода
Взяв образец с сайта asksensors.com, я собрал весь код так, чтобы он был самодостаточным, чтобы вам было легко им пользоваться (его можно взять в конце поста).
В коде я удалил ссылки на мой настоящий поддомен Центра Интернета вещей (заменив его на XXXX) и идентификатор устройства (заменив на YYYY).
Пароль MQTT сделан постоянным, я, конечно, использую динамическую генерацию ключа SAS, но для демонстрации я упростил константу, срок действия которой истекает к моменту, когда вы это читаете).
Вопросы и проблемы, требующие устранения
Конечно, код не работает (я имею в виду часть TLS), так как я не предоставил ключ.
Поэтому мои вопросы:
- а) что указать в константе
my_key
; - б) и как его создать?
И вообще, можно ли это сделать, чтобы он работал через Ethernet?
Текущие журналы
Журналы следующие:
ets Jul 29 2019 12:21:46
rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
*****************************************************
* IoT hub over MQTT port 8883
********** connecting to Ethernet : DHCP ok
********** Attempting MQTT connection…
(SSLClient)(SSL_ERROR)(m_run_until): SSL internals timed out! This could be an internal error, bad data sent from the server, or data being discarded due to a buffer overflow. If you are using Ethernet, did you modify the library properly (see README)?
(SSLClient)(SSL_ERROR)(connected): Not connected because write error is set
(SSLClient)(SSL_ERROR)(m_print_ssl_error): SSL_BR_WRITE_ERROR
(SSLClient)(SSL_ERROR)(m_start_ssl): Failed to initlalize the SSL layer
(SSLClient)(SSL_ERROR)(m_print_br_error): Unknown error code: 0
failed, rc=-2-> try again in 5 seconds
Код для исправления
Код выглядит следующим образом:
/*
* MQTT and Microsoft Azure IoT Hub Platform
* @history UPDATED by SDL (Steven de Luca) on Tuesday November 8th 2022
* @history derived from:
* MQTT and AskSensors IoT Platform
* Description: Arduino Ethernet publishes data to AskSensors using MQTT
* Author: https://asksensors.com, 2020 г.
* github: https://github.com/asksensors
*/
#include <SPI.h>
#include <PubSubClient.h>
#include <Ethernet.h>
#include "SSLClient.h"
#ifndef _CERTIFICATES_H_
#define _CERTIFICATES_H_
#ifdef __cplusplus
extern "C"
{
#endif
/* This file is auto-generated by the pycert_bearssl tool. Do not change it manually.
* Certificates are BearSSL br_x509_trust_anchor format. Included certs:
*
* Index: 0
* Label: Baltimore CyberTrust Root
* Subject: CN=Baltimore CyberTrust Root,OU=CyberTrust,O=Baltimore,C=IE
* Domain(s): XXXX.azure-devices.net
*/
#define TAs_NUM 1
static const unsigned char TA_DN0[] = {
0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31,
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79,
0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20,
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69,
0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72,
0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74,
};
static const unsigned char TA_RSA_N0[] = {
0xa3, 0x04, 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a,
0xb5, 0x79, 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3,
0x5b, 0x8e, 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09,
0x05, 0x6d, 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88,
0xda, 0x12, 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52,
0x7b, 0x88, 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a,
0x09, 0xe7, 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d,
0x2d, 0xe5, 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea,
0xf5, 0xab, 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f,
0x0c, 0xd5, 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70,
0xf0, 0x8f, 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33,
0x7a, 0x77, 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13,
0xd2, 0xc0, 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc,
0xb4, 0xdd, 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5,
0x63, 0xe0, 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea,
0xeb, 0xd4, 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69,
0xbc, 0xf9, 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9,
0x90, 0x2c, 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98,
0x21, 0x5c, 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86,
0x3a, 0x6b, 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78,
0x8d, 0x76, 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90,
0xdc, 0x27, 0x1a, 0x39,
};
static const unsigned char TA_RSA_E0[] = {
0x01, 0x00, 0x01,
};
static const br_x509_trust_anchor TAs[] = {
{
{ (unsigned char *)TA_DN0, sizeof TA_DN0 },
BR_X509_TA_CA,
{
BR_KEYTYPE_RSA,
{ .rsa = {
(unsigned char *)TA_RSA_N0, sizeof TA_RSA_N0,
(unsigned char *)TA_RSA_E0, sizeof TA_RSA_E0,
} }
}
},
};
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ifndef _CERTIFICATES_H_ */
// Конфигурация хоста MQTT
const char *mqtt_server = "XXXX.azure-devices.net";
//MQTT v3.1.1 на порту 8883
//MQTT v3.1.1 через WebSocket на порту 443.
unsigned int mqtt_port =
8883
//443 через сокеты
;
const char *deviceId = "YYYY";
const char *username = "XXXX.azure-devices.net/YYYY/?api-version=2020-09-30&dct=azsdk-c%2F1.3.0-beta.2";
const char *password = "SharedAccessSignature sr=XXXX.azure-devices.net%2Fdevices%2FYYYY&sig=x9xm%2FVmtXJlLe2kWZd1LjRyGzUUXVDZlGM3Z%2BvjyPZc%3D&se=3618";
const char* pubTopic = "install/..../....."; // публикация/имя пользователя/apiKeyIn
const unsigned int writeInterval = 25000; // интервал записи (в мс)
// == MAC-адрес
byte mac[] = {
0x90, 0x38, 0x0C, 0x9F, 0x75, 0x84
};
// == Установите статический IP-адрес, который будет использоваться, если DHCP не сможет назначить
IPAddress ip(192, 168, 1, 123); // TODO: удалить резервный статический IP-адрес в случае сбоя DHCP
EthernetClient ethernetClient;
// Сертификат клиента, может быть в формате PEM или DER
// Формат DER будет массивом необработанных байтов, а формат PEM будет строкой
// Как указано в https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-mqtt-support: "Вы можете
// создайте этот файл, скопировав информацию о сертификате из certs.c в
// SDK Azure IoT для C. Включите строки -----BEGIN CERTIFICATE----- и
// -----КОНЕЦ СЕРТИФИКАТА-----""
// Итак, из Azure https://github.com/Azure/azure-iot-sdk-c/blob/main/certs/certs.c
const char my_cert[] =
/* Baltimore CyberTrust Root */
"-----BEGIN CERTIFICATE-----\r\n"
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n"
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n"
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n"
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n"
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n"
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n"
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n"
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n"
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n"
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n"
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n"
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n"
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n"
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n"
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n"
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n"
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n"
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n"
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n"
"-----END CERTIFICATE-----\r\n";
// Закрытый ключ клиента должен быть того же формата, что и сертификат клиента
// Поддерживаются как RSA, так и ECC, ECC показан ниже
const char my_key[] =
"-----BEGIN EC PRIVATE KEY-----\n"
"-----END EC PRIVATE KEY-----\n";
// Эта строка будет анализировать и сохранять указанную выше информацию, чтобы SSLClient мог использовать ее позже
// Замените `fromPEM` на `fromDER`, если вы используете сертификаты в формате DER.
SSLClientParameters mTLS = SSLClientParameters::fromPEM(my_cert, sizeof(my_cert), my_key, sizeof(my_key));
SSLClient sslClient(ethernetClient, TAs, 2, A7);
PubSubClient client(sslClient);
void setup() {
Serial.begin(115200);
Serial.println("*****************************************************");
Serial.printf ("* ZZZZ IoT hub over MQTT port %d", mqtt_port); Serial.println();
Serial.print ("********** connecting to Ethernet : ");
// запускаем соединение Ethernet:
Ethernet.init(5);
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// попробуйте настроить с использованием IP-адреса вместо DHCP:
Ethernet.begin(mac, ip);
Serial.println("Static IP ok");
}
else {
Serial.println("DHCP ok");
}
// даем Ethernet Shield секунду для инициализации:
delay(1000);
sslClient.setMutualAuthParams(mTLS);
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
void loop() {
if (!client.connected())
reconnect();
client.loop();
Serial.println("********** Publish MQTT data");
char mqtt_payload[30] = "";
snprintf(mqtt_payload, 100, "m1=%ld&m2=%ld", random(10, 100), random(10, 100));
Serial.print("Publish message: ");
Serial.println(mqtt_payload);
client.publish(pubTopic, mqtt_payload);
Serial.println("> MQTT data published");
Serial.println("********** End ");
Serial.println("*****************************************************");
delay(writeInterval); // задерживать
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void reconnect() {
// Цикл, пока мы снова не подключимся
while (!client.connected()) {
Serial.print("********** Attempting MQTT connection…");
// Попытка подключения
if (client.connect(deviceId, username, password)) {
Serial.println("-> MQTT client connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println("-> try again in 5 seconds");
// Подождите 5 секунд перед повторной попыткой: TODO: DEBUG подождите одну минуту, пока я нашел способ справиться с сертификатом
delay(60 * 1000);
}
}
}
0
- Ethernet nodemcu
- Подключение ESP32 через MQTT
- MQTT на nano с Ethernet Shield не работает
- Несколько тем MQTT
- Обратный вызов подписки MQTT не может быть вызван в режиме глубокого сна ESP8266
- Arduino не подключается к локальному брокеру MQTT
- Пробуждение ESP8266 от Light-Sleep с помощью сигнала от MQTT?
- как перевести json в строку?
Я рекомендую вам использовать библиотеку lwIP_w5500. в комплекте с esp8266 Arduino версии 3+, @Juraj