Month: October 2013

M2Mqtt : intervento sul Keep Alive previsto dal protocollo MQTT

A seguito di una segnalazione del mio amico Lorenzo Maiorfi, nonché MVP Microsoft, ho apportato un bug fix alla mia libreria M2Mqtt (MQTT client) che permette di gestire al meglio il caso in cui il client non trasmette nulla per un tempo pari al “keep alive period” ed il keep alive thread, il cui compito è quello di pingare il broker per tenere attiva la connessione, non riceve nulla entro il timeout (es. broker non online, disconnessione dalla rete, …).

L’aggiornamento è già disponibile su CodePlex e su Nuget !

Advertisements

Visual Studio 2012 : blocco durante creazione nuovo progetto

Oggi mi è capitato di aprire Visual Studio 2012, cliccare su “New Project” e scoprire magicamente che …. si blocca e non mi mostra l’elenco dei template !!

Il problema è tipicamente dovuto alla lista dei template che viene “sporcata” da tutti gli SDK che installiamo. Per risolverlo, ho fatto in modo che Visual Studio rigenerasse la cache dei template, in questo modo :

  1. Aprire “Developer Comand Prompt for Visual Studio 2012” con i diritti di amministratore (lo trovate sotto “Visual Studio Tools”);
  2. Eseguire “devenv /setup”;
  3. Attendere circa 10 minuti affinché il processo venga completato;

Al riavvio di Visual Studio…tutto ha ripreso a funzionare !!

uPLibrary : HttpClient aggiornato con il supporto per IPv6

Ho appena aggiornato su CodePlex e su Nuget la libreria uPLibrary (versione 2.1.0.0), aggiungendo nel client HTTP (classe HttpClient) il rilevamento automatico di un eventuale indirizzo IPv6 a cui connettersi.

Per rilevare la classe di indirizzo (AddressFamily), ho implementato l’ “Extension Method” GetAddressFamily() per la classe IPAddress attraverso la classe statica IPAddressUtility, necessario solo perché il .Net Micro Framework non fornisce nativamente  la property AddressFamily della classe IPAddress.

public static AddressFamily GetAddressFamily(this IPAddress ipAddress)
{
   return (ipAddress.ToString().IndexOf(':') != -1) ?
	   AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
}

In questo modo, al momento della creazione della socket, viene automaticamente  impostata la corretta address family a partire dall’indirizzo a cui connettersi.

// create socket and connect
this.socket = new Socket(
	this.hostIpEndPoint.Address.GetAddressFamily(),
	SocketType.Stream, ProtocolType.Tcp);
this.socket.Connect(this.hostIpEndPoint);

MQTT su SSL/TLS con la libreria M2Mqtt ed il broker Mosquitto

Dopo aver rilasciato la nuova versione della mia libreria M2Mqtt con il supporto per l’SSL/TLS con autenticazione lato server, è giunto il momento di mostrarvi un esempio di utilizzo.

Scegliamo ed installiamo il broker : Mosquitto

In primo luogo dobbiamo scegliere un broker MQTT tra quelli disponibili ma purtroppo nessuno è sviluppato utilizzando il .Net Framework. Scartando soluzioni più complesse, soprattutto per quanto riguarda l’installazione e la configurazione, come IBM Websphere, possiamo prendere in considerazione dei broker come RabbitMQ, HiveMQ oppure Mosquitto. In questo caso, la mia scelta è caduta proprio su Mosquitto che possiamo scaricare dal sito ufficiale per vari sistemi operativi basati su Linux, così come è presente un comodissimo installer per l’ambiente Windows. In realtà, per Windows esistono due modalità di installazione : la prima utilizza un installer in cui il broker è compilato in maniera nativa mentre nel secondo caso ci si basa su Cygwin che fornisce un ambiente Linux-like per Windows. E’ ovvio che la prima modalità è la più semplice ed immediata. Al termine dell’installazione, oltre ad essere installato il broker vero e proprio (mosquitto.exe), saranno forniti anche due applicazioni console che rappresentano un publisher (mosquitto_pub.exe) ed un subscriber (mosquitto_sub), molto utili per testare il funzionamento del broker stesso. L’utilizzo di Mosquitto è molto ben documentato sul sito ufficiale ma ciò che noi interessa sapere è soprattutto la parte di configurazione legata all’SSL/TLS. Per modificare le impostazioni di default del broker, quest’ultimo può essere lanciato specificando un file di configurazione (un file di esempio mosquitto.conf è presente nella cartella di installazione). Prima di affrontare tale argomento, è necessario generare i certificati della CA (Certification Authority) e del server/broker che andremo ad utilizzare nel corso dell’esempio.

Generazione dei certificati : CA e broker

In questo post, non voglio affrontare l’argomento complesso relativo al protocollo SSL/TLS ma ne voglio almeno sottolineare gli aspetti principali più strettamente legati all’esempio che andremo a realizzare. Il server che ospita il broker necessita di un certificato X.509 che deve essere opportunamente firmato da una CA (Certification Authority). Nel nostro caso, per evitare di “acquistare” un certificato e farlo firmare da una vera CA, andremo a generare un certificato “self-signed” root per la CA attraverso il quale poi firmeremo quello del server. La generazione e firma dei certificati può essere effettuata in due modi : attraverso il pacchetto OpenSSL, il cui installer per Windows può essere scaricato qui, oppure attraverso il tool Makecert che ritroviamo nell’ambito dell’installazione del nostro “caro” Visual Studio. Utilizzando per lavoro OpenSSL, ho preferito puntare su quest’ultimo, anche perché è molto più completo. L’installer da scaricare è quello relativo alla versione 1.0.1e (Win32 o Win64 dipende dal vostro sistema operativo) che necessita del Visual C++ 2008 Redistributables package. Al termine dell’installazione (tipicamente nella cartella C:\OpenSSL-WinXX con XX=32 oppure 64), può accadere (come si è verificato a me) che lanciando il comando openssl dal prompt dei comandi (all’interno della cartella bin di installazione), vi venga mostrato il seguente warning :

WARNING : can’t open config file: /usr/local/ssl/openssl.cfg

Unable to load config info from /usr/local/ssl/openssl.cfg

La soluzione immediata prevede di aggiungere una variabile di ambiente in cui definire il path nel quale si trova tale file di configurazione :

set OPENSS_CONF=C:\OpenSSL-Win64\bin\openssl.cfg

Lo so, avete ragione, non è normale che un’installazione non termini nel migliore dei modi…e sono perfettamente d’accordo !! Ma superato questo scoglio, OpenSSL è sicuramente il migliore ! Tutte le operazioni che seguono, vanno eseguite dal prompt dei comandi all’interno della cartella C:\OpenSSL-Win64\bin\PEM, nella quale OpenSSL gestisce il suo store di certificati.

In primo luogo, generiamo il certificato della CA con il seguente comando :

openssl req -new -x509 -days 3650 -keyout m2mqtt_ca.key -out m2mqtt_ca.crt

attraverso il quale otterremo una chiave privata per la CA (m2mqtt_ca.key ed il certificato “self-signed” m2mqtt_ca.crt). Verrà richiesta una “pass phrase” per crittografare anche la chiave privata ed in successione tutte le informazioni relative al certificato (Country Name, State, …) tra le quale ho preferito impostare come CN (Common Name) il nome del mio PC (questa impostazione è più strettamente necessaria al momento della generazione del certificato server).01_cert_ca

A questo punto possiamo passare alla generazione di una chiave privata per il server e la relativa richiesta di certificato che dovrà essere firmata dalla CA. Per quanto riguarda la generazione della chiave privata, possiamo eseguire :

openssl genrsa -des3 -out m2mqtt_srv.key 1024

attraverso il quale otterremo la chiave privata m2mqtt_srv.key del server crittografata mediante una “pass phrase”.

02_key_srv

Fatto ciò, passiamo alla generazione della richiesta di certificato da parte del server che dovrà essere firmato dalla CA.

openssl req -out m2mqtt_srv.csr -key m2mqtt_srv.key -new

In questo modo generiamo la richiesta m2mqtt_srv.csr (attenzione ! non è ancora il certificato firmato) all’interno della quale ci sono tutte le informazioni del server (richieste dall’esecuzione del comando) tra le quali il CN va impostato uguale al nome del server stesso che fornirà il certificato all’atto delle richieste da parte dei client. Nel mio caso, poiché il broker sarà eseguito sul mio PC, il Common Name è uguale al nome del mio PC “ppatierno-PC”.

03_csr_srv

L’ultimo passo consiste nel firmare la richiesta del server attraverso la CA ed ottenere il certificato definitivo del broker.

openssl x509 -req -in m2mqtt_srv.csr -CA m2mqtt_ca.crt -CAkey m2mqtt_ca.key -CAcreateserial -out m2mqtt_srv.crt -days 3650

Abbiamo così completato tutti i passaggi necessari alla generazione dei certificati.

04_cert_srv

In realtà, preferisco anticipare un passo. I certificati appena generati sono in formato PEM ma il .Net Framework, attraverso la classe SslStream (utilizzata nella M2Mqtt library), utilizza i certificati in formato DER. Il certificato che dobbiamo convertire è ovviamente quello della CA che va installato sul client, il quale invece riceverà quello del server da verificare durante l’handshake SSL. La conversione può essere eseguita sempre attraverso OpenSSL nel modo seguente :

openssl x509 -outform der -in m2mqtt_ca.crt -out m2mqtt_ca.der

Abbiamo finalmente tutto ciò che ci serve per poter configurare il broker !

Configurazione ed avvio del broker

Per la configurazione del broker, consiglio di fare una copia del file di esempio mosquitto.conf che andremo a modificare. Nel mio caso, ho creato il file mosquitto_m2mqtt.conf nel quale i parametri da modificare sono i seguenti all’interno della sezione “Default Listener” e “Certificate based SSL/TLS support” :

  • bind_address : va impostato al nome del server (nel mio caso bind_address ppatierno-PC);
  • port : la porta del protocollo MQTT per SSL/TLS è la 8883 (port 8883);
  • cafile : definisce il path del certificato della CA (cafile C:\OpenSSL-Win64\bin\PEM\m2mqtt_ca.crt);
  • certfile : path al certificato del server (certfile C:\OpenSSL-Win64\bin\PEM\m2mqtt_srv.crt);
  • keyfile : path della chiave privata del server (keyfile C:\OpenSSL-Win64\bin\PEM\m2mqtt_srv.key);
  • tls_version : versione del protocollo TLS (tls_version tlsv1);

Una volta terminate le impostazioni, il broker Mosquitto può essere lanciato dal prompt dei comandi (eseguito come amministratore) nel modo seguente :

mosquitto –c mosquitto_m2mqtt.conf –v

Attraverso il parametro –c specifichiamo il file di configurazione da usare mentre –v attiva la modalità “verbose” per visualizzare i messaggi di debug. Verrà richiesta la “pass phrase” della chiave privata del server necessaria durante l’handshake SSL/TLS nell’ambito dell’utilizzo dell’algoritmo di cifratura asimmetrica RSA.

05_broker_start

Nel mio caso, è stato fatto il binding all’indirizzo IPv6 del mio PC in corrispondenza del quale il broker si mette in ascolto sulla porta 8883. Abbiamo così il broker in esecuzione pronto per ricevere messaggi e distribuire i messaggi tra i vari client MQTT.

Avvio di un subscriber e test del broker

Poiché nel corso dell’esempio, il client che utilizzerà la M2Mqtt library farà da publisher dei messaggi, utilizziamo il tool mosquitto_sub per la parte di subscriber in modo da poter anche testare immediatamente se il broker funziona correttamente. Sempre da un prompt dei comandi (eseguito come amministratore) all’interno della cartella di installazione di Mosquitto, lanciamo il client nel modo seguente :

mosquitto_sub -h ppatierno-PC -p 8883 -q 1 -t sensor/temp –cafile C:\OpenSSL-Win64\bin\PEM\m2mqtt_ca.crt –tls-version tlsv1 -d

Abbiamo specificato l’host e la porta al quale connettersi, il path del file certificato della CA per verificare il certificato server ricevuto durante l’handshake SSL/TLS, la versione TLS, il topic al quale sottoscriversi con il corrispondente livello di QoS ed infine la flag che attiva il debug per visualizzare i messaggi scambiati con il broker.

06_sub_connect

Lo scambio dei messaggi per la connessione (CONNECT, CONNACK) ed il ping per mantenere viva la connessione tra client e broker (PINREQ, PINGRESP) è visibile anche nella console in cui stiamo eseguendo il broker stesso.

07_sub_broker_connect

Installazione certificato della CA

Le classi del .Net Framework che permettono di gestire le connessioni SSL/TLS, come la classe SslStream, fanno uso della SSPI (Security Support Provider Interface) di Windows per la gestione dei certificati. Ciò vuol dire che, prima di utilizzare il certificato della CA all’interno della nostra applicazione client, è necessario installarlo nel nostro sistema operativo. Uno dei modi più semplici per accedere al wizard di import dei certificati è attraverso le “Opzioni Internet” per importare il certificato tra le “Autorità di certificazione radice attendibili”.

08_import_der

Ricordiamoci che è necessario importare il certificato nel formato DER.

Nel caso in cui il client sia sviluppato con il .Net Micro Framework, se fosse eseguito nell’emulatore, tale operazione sarebbe ancora necessaria, in quanto l’emulatore utilizza la medesima interfaccia verso il sistema operativo. Ovviamente, solo nel caso in cui l’applicazione sia eseguita su una board reale, il certificato della CA andrebbe semplicemente copiato su un supporto (es. Flash, scheda SD, …) per poi essere letto a runtime dall’applicazione stessa. Nel nostro caso, eseguiremo l’applicazione su un PC con il .Net Framework (il risultato sarebbe lo stesso eseguendo la medesima applicazione con il .Net Micro Framework sull’emulatore).

Realizziamo il client e … pubblichiamo i messaggi criptati !

L’applicazione client, che utilizza la M2Mqtt library, non fa altro che connettersi al broker e pubblicare un dato finto di temperatura sul topic “sensor/temp” (al quale abbiamo sottoscritto un altro client al paragrafo precedente). Il certificato della CA è stato incluso in un file di risorse (Resources.resx) ed, una volta letto come stream di byte, viene fornito al costruttore della classe X509Certificate, in modo da ricavare un certificato X.509 valido per l’MqttClient.

class Program
{
	static void Main(string[] args)
	{
		MqttClient client = new MqttClient("ppatierno-PC",
			MqttClient.MQTT_BROKER_DEFAULT_SSL_PORT,
			true,
			new X509Certificate(Resources.m2mqtt_ca));

		client.Connect(Guid.NewGuid().ToString());

		for (int i = 0; i < 1000; i++)
			client.Publish("sensor/temp", Encoding.UTF8.GetBytes("27"), MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE);

		Thread.Sleep(Timeout.Infinite);
	}
}

Nelle finestre console del broker si evince la pubblicazione e lo scambio dei messaggi MQTT relativo al QoS 1 (Exactly Once).

09_pub_broker

Nella finestra console del subscriber è possibile osservare il messaggio ricevuto dal publisher.

10_pub_sub

Conclusioni

Il protocollo MQTT non fornisce in maniera intrinseca funzionalità di sicurezza, per cui è necessario affidarsi a ciò che offre lo strato di trasporto su cui viaggiano i messaggi MQTT, ossia il TCP/IP. In questo caso, uno dei protocolli maggiormente utilizzati è SSL/TLS che mette a disposizione anche l’autenticazione del server oltre alla criptazione dei dati. E’ ovvio che tale strato, riduce le performance dell’intero sistema e la velocità con la quale i messaggi sono scambiati. Un’ulteriore possibilità potrebbe essere quella di applicare la criptazione direttamente sui dati contenuti nel messaggio MQTT ma ciò prevede l’utilizzo di un algoritmo di criptazione simmetrico stabilito a priori e per il quale i client hanno tutti conoscenza della chiave. Questa modalità, però, eliminerebbe l’autenticazione del server che andrebbe realizzata a livello applicativo in altro modo. In molti casi, comunque, l’adozione di una tecnica rispetto ad un’altra, dipende molto dalle potenzialità del nostro sistema che nel caso di sistemi embedded potrebbe non supportare l’SSL/TLS per una questione di costo di elaborazione ed occupazione di memoria delle librerie di criptazione.

M2Mqtt : supporto connessione ad un broker in IPv6

Ho appena aggiornato soltanto su CodePlex (per adesso) la libreria M2Mqtt (versione 2.1.0.0), aggiungendo il rilevamento automatico di un eventuale indirizzo IPv6 a cui connettersi.

Per rilevare la classe di indirizzo (AddressFamily), ho implementato l’ “Extension Method” GetAddressFamily() per la classe IPAddress attraverso la classe statica IPAddressUtility, necessario solo perché il .Net Micro Framework non fornisce nativamente  la property AddressFamily della classe IPAddress. Infatti, nel caso di full/compact .Net Framework viene restituita tale proprietà mentre nel caso del .Net Micro Framework viene fatto una semplice ricerca del carattere “:” (separatore negli indirizzi IPv6).

public static AddressFamily GetAddressFamily(this IPAddress ipAddress)
{
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
    return ipAddress.AddressFamily;
#else
    return (ipAddress.ToString().IndexOf(':') != -1) ?
        AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
#endif
}

In questo modo, al momento della creazione della socket, viene automaticamente impostata la corretta address family a partire dall’indirizzo a cui connettersi.

this.socket = new Socket(this.remoteIpAddress.GetAddressFamily(), SocketType.Stream, ProtocolType.Tcp);

Piccolo cambiamento….molto utile !

M2Mqtt library : aggiunto il supporto per SSL/TLS !!

m2mqttssl

Ho finalmente avuto modo di aggiungere ciò che volevo da tempo alla mia libreria M2Mqtt : il supporto al protocollo SSL/TLS !

Grazie a questa nuova funzionalità, è ora possibile collegarsi ad un broker MQTT utilizzando anche il protocollo SSL/TLS (per la versione .Net Micro Framework c’è il supporto solo fino a TLS1.0) per poter usufruire delle sue principali caratteristiche : criptazione dei dati ed autenticazione del server attraverso un certificato X509 (non è supportata l’autenticazione lato client).

Per poter lasciare libera scelta allo sviluppatore di voler includere o meno questa funzionalità nel proprio progetto, ho vincolato il tutto al simbolo di compilazione “SSL” che deve essere definito per aggiungere tale supporto. In questo modo, per alcune piattaforme con meno memoria (vedi Netduino o FEZ Cerberus nel caso di .Net Micro Framework), è possibile escluderla, ricordando di eliminare anche i riferimenti agli assemblies che gestiscono l’SSL/TLS.

Ovviamente, oltre che su CodePlex, l’aggiornamento è disponibile anche su Nuget !

Al più presto, scriverò un nuovo post in cui descriverò un esempio di utilizzo della connessione sicura in ambito MQTT con un broker come Mosquitto !