Sur cette page

Client CoAP rapide et sécurisé

Sécurisation du client CoAP et utilisation de celui-ci avec une interface utilisateur d'Qt Quick.

Client CoAP sécurisé avec configuration de certificat et réponse

Le client CoAP sécurisé rapide montre comment créer un client CoAP sécurisé et l'utiliser dans une application Qt Quick.

Remarque : dans sa version actuelle,Qt CoAP ne fournit pas d’API QML. Cependant, vous pouvez rendre les classes C++ du module accessibles à QML, comme le montre l’exemple.

Exécution de l’exemple

Vous pouvez exécuter l’exemple à partir de :

Pour exécuter l’application d’exemple, vous devez d’abord configurer un serveur CoAP sécurisé. Vous pouvez exécuter l’exemple avec n’importe quel serveur CoAP sécurisé prenant en charge l’un des modes d’authentification suivants : clé pré-partagée (PSK) ou certificat. Pour plus d’informations sur la configuration d’un serveur CoAP sécurisé, consultez la section « Configuration d’un serveur CoAP sécurisé ».

Exposition des classes C++ à QML

Dans cet exemple, vous devez exposer la classe ` QCoapClient ` et l’espace de noms ` QtCoap ` à QML. Pour ce faire, créez une classe wrapper personnalisée et utilisez les macros d’enregistrement spéciales.

Créez la classe ` QmlCoapSecureClient ` comme wrapper autour de ` QCoapClient`. Cette classe contient également le mode de sécurité sélectionné et les paramètres de configuration de sécurité. Utilisez la macro ` Q_INVOKABLE ` pour exposer plusieurs méthodes à QML. Utilisez également la macro ` QML_NAMED_ELEMENT ` pour enregistrer la classe dans QML sous le nom ` CoapSecureClient`.

class QmlCoapSecureClient : public QObject
{
    Q_OBJECT
    QML_NAMED_ELEMENT(CoapSecureClient)

public:
    QmlCoapSecureClient(QObject *parent = nullptr);
    ~QmlCoapSecureClient() override;

    Q_INVOKABLE void setSecurityMode(QtCoap::SecurityMode mode);
    Q_INVOKABLE void sendGetRequest(const QString &host, const QString &path, int port);
    Q_INVOKABLE void setSecurityConfiguration(const QString &preSharedKey, const QString &identity);
    Q_INVOKABLE void setSecurityConfiguration(const QString &localCertificatePath,
                                              const QString &caCertificatePath,
                                              const QString &privateKeyPath);
    Q_INVOKABLE void disconnect();

Q_SIGNALS:
    void finished(const QString &result);

private:
    QCoapClient *m_coapClient;
    QCoapSecurityConfiguration m_configuration;
    QtCoap::SecurityMode m_securityMode;
};

Ensuite, enregistrez l’espace de noms QtCoap afin de pouvoir utiliser les énumérations qui y sont fournies :

namespace QCoapForeignNamespace
{
    Q_NAMESPACE
    QML_FOREIGN_NAMESPACE(QtCoap)
    QML_NAMED_ELEMENT(QtCoap)
}

Modification des fichiers de compilation

Pour que les types personnalisés soient accessibles depuis QML, mettez à jour les fichiers du système de compilation en conséquence.

CMake

Pour une compilation basée sur CMake, ajoutez ce qui suit au fichier ` CMakeLists.txt` :

qt_add_qml_module(quicksecureclient
    URI CoapSecureClientModule
    SOURCES
        qmlcoapsecureclient.cpp qmlcoapsecureclient.h
    QML_FILES
        FilePicker.qml
        Main.qml
)
qmake

Pour une compilation avec qmake, modifiez le fichier quicksecureclient.pro de la manière suivante :

CONFIG += qmltypes
QML_IMPORT_NAME = CoapSecureClientModule
QML_IMPORT_MAJOR_VERSION = 1
    ...
qml_resources.files = \
    qmldir \
    FilePicker.qml \
    Main.qml

qml_resources.prefix = /qt/qml/CoapSecureClientModule

RESOURCES += qml_resources

Utilisation des nouveaux types QML

Désormais, les classes C++ étant correctement exposées à QML, vous pouvez utiliser les nouveaux types.

Création du client

CoapSecureClient est instancié à partir du fichier ` Main.qml `. Il gère le signal ` QmlCoapSecureClient::finished() ` et met à jour l’interface utilisateur en conséquence :

CoapSecureClient {
    id: client
    onFinished: (result) => {
        outputView.text = result;
        statusLabel.text = "";
        disconnectButton.enabled = true;
    }
}

L’instance de ` QCoapClient ` est créée lorsque l’utilisateur sélectionne ou modifie le mode de sécurité dans l’interface utilisateur. La méthode ` QmlCoapSecureClient::setSecurityMode() ` est appelée depuis le code QML lorsqu’un des modes de sécurité est sélectionné :

ButtonGroup {
    id: securityModeGroup
    onClicked: {
        if ((securityModeGroup.checkedButton as RadioButton) === preSharedMode)
            client.setSecurityMode(QtCoap.SecurityMode.PreSharedKey);
        else
            client.setSecurityMode(QtCoap.SecurityMode.Certificate);
    }
}

Côté C++, cette méthode crée un objet ` QCoapClient ` et se connecte à ses signaux ` finished()` et ` error()`. La classe gère ces deux signaux en interne et les transmet au nouveau signal ` finished() `.

void QmlCoapSecureClient::setSecurityMode(QtCoap::SecurityMode mode)
{
    // Create a new client, if the security mode has changed
    if (m_coapClient && mode != m_securityMode) {
        delete m_coapClient;
        m_coapClient = nullptr;
    }

    if (!m_coapClient) {
        m_coapClient = new QCoapClient(mode);
        m_securityMode = mode;

        connect(m_coapClient, &QCoapClient::finished, this,
                [this](QCoapReply *reply) {
                    if (!reply)
                        emit finished(tr("Something went wrong, received a null reply"));
                    else if (reply->errorReceived() != QtCoap::Error::Ok)
                        emit finished(errorMessage(reply->errorReceived()));
                    else
                        emit finished(reply->message().payload());
                });

        connect(m_coapClient, &QCoapClient::error, this,
                [this](QCoapReply *, QtCoap::Error errorCode) {
                    emit finished(errorMessage(errorCode));
                });
    }
}
Envoi d’une requête

Cliquez sur le bouton « Send Request » pour définir la configuration de sécurité en fonction du mode de sécurité sélectionné et envoyer une requête « GET » :

Button {
    id: requestButton
    text: qsTr("Send Request")
    enabled: securityModeGroup.checkState !== Qt.Unchecked

    onClicked: {
        outputView.text = "";
        if ((securityModeGroup.checkedButton as RadioButton) === preSharedMode)
            client.setSecurityConfiguration(pskField.text, identityField.text);
        else
            client.setSecurityConfiguration(localCertificatePicker.selectedFile,
                                            caCertificatePicker.selectedFile,
                                            privateKeyPicker.selectedFile);

        client.sendGetRequest(hostComboBox.editText, resourceField.text,
                              parseInt(portField.text));

        statusLabel.text = qsTr("Sending request to %1%2...").arg(hostComboBox.editText)
                                                             .arg(resourceField.text);
    }
}

Il existe deux surcharges pour la méthode setSecurityConfiguration.

La surcharge pour le mode PSK se contente de définir l’identité du client et la clé pré-partagée :

void
QmlCoapSecureClient::setSecurityConfiguration(const QString &preSharedKey, const QString &identity)
{
    QCoapSecurityConfiguration configuration;
    configuration.setPreSharedKey(preSharedKey.toUtf8());
    configuration.setPreSharedKeyIdentity(identity.toUtf8());
    m_configuration = configuration;
}

Quant à la surcharge pour les certificats X.509, elle lit les fichiers de certificats et la clé privée, puis définit la configuration de sécurité :

void QmlCoapSecureClient::setSecurityConfiguration(const QString &localCertificatePath,
                                                   const QString &caCertificatePath,
                                                   const QString &privateKeyPath)
{
    QCoapSecurityConfiguration configuration;

    const auto localCerts =
            QSslCertificate::fromPath(QUrl(localCertificatePath).toLocalFile(), QSsl::Pem,
                                      QSslCertificate::PatternSyntax::FixedString);
    if (localCerts.isEmpty())
        qCWarning(lcCoapClient, "The specified local certificate file is not valid.");
    else
        configuration.setLocalCertificateChain(localCerts.toVector());

    const auto caCerts = QSslCertificate::fromPath(QUrl(caCertificatePath).toLocalFile(), QSsl::Pem,
                                                   QSslCertificate::PatternSyntax::FixedString);
    if (caCerts.isEmpty())
        qCWarning(lcCoapClient, "The specified CA certificate file is not valid.");
    else
        configuration.setCaCertificates(caCerts.toVector());

    QFile privateKey(QUrl(privateKeyPath).toLocalFile());
    if (privateKey.open(QIODevice::ReadOnly)) {
        QCoapPrivateKey key(privateKey.readAll(), QSsl::Ec);
        configuration.setPrivateKey(key);
    } else {
        qCWarning(lcCoapClient) << "Unable to read the specified private key file"
                                << privateKeyPath;
    }
    m_configuration = configuration;
}

Une fois la configuration de sécurité définie, la méthode ` sendGetRequest ` définit l’URL de la requête et envoie une requête ` GET ` :

void QmlCoapSecureClient::sendGetRequest(const QString &host, const QString &path, int port)
{
    if (!m_coapClient)
        return;

    m_coapClient->setSecurityConfiguration(m_configuration);

    QUrl url;
    url.setHost(host);
    url.setPath(path);
    url.setPort(port);
    m_coapClient->get(url);
}

Lors de l’envoi de la première requête, une poignée de main avec le serveur CoAP est effectuée. Une fois la poignée de main réussie, tous les messages suivants sont chiffrés, et modifier la configuration de sécurité après une poignée de main réussie n’aura aucun effet. Si vous souhaitez la modifier, ou changer d’hôte, vous devez d’abord vous déconnecter.

void QmlCoapSecureClient::disconnect()
{
    if (m_coapClient)
        m_coapClient->disconnect();
}

Cela interrompra la procédure d'établissement de la connexion et fermera les sockets ouverts.

Pour l’authentification à l’aide de certificats X.509, les fichiers de certificats doivent être spécifiés. Le composant « FilePicker » est utilisé à cette fin. Il combine un champ de texte et un bouton permettant d’ouvrir une boîte de dialogue de sélection de fichier lorsque l’on clique dessus :

Item {
    id: filePicker

    property string dialogText
    property alias selectedFile: filePathField.text

    height: addFileButton.height

    FileDialog {
        id: fileDialog
        title: qsTr("Please Choose %1").arg(filePicker.dialogText)
        currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
        fileMode: FileDialog.OpenFile
        onAccepted: filePathField.text = fileDialog.selectedFile
    }

    RowLayout {
        anchors.fill: parent
        TextField {
            id: filePathField
            placeholderText: qsTr("<%1>").arg(filePicker.dialogText)
            inputMethodHints: Qt.ImhUrlCharactersOnly
            selectByMouse: true
            Layout.fillWidth: true
        }

        Button {
            id: addFileButton
            text: qsTr("Add %1").arg(filePicker.dialogText)
            onClicked: fileDialog.open()
        }
    }
}

FilePicker est instancié plusieurs fois dans le fichier « Main.qml » afin de créer des champs de saisie pour les certificats et la clé privée :

FilePicker {
    id: localCertificatePicker
    dialogText: qsTr("Local Certificate")
    enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
    Layout.columnSpan: 2
    Layout.fillWidth: true
}

FilePicker {
    id: caCertificatePicker
    dialogText: qsTr("CA Certificate")
    enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
    Layout.columnSpan: 2
    Layout.fillWidth: true
}

FilePicker {
    id: privateKeyPicker
    dialogText: qsTr("Private Key")
    enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
    Layout.columnSpan: 2
    Layout.fillWidth: true
}

Configuration d’un serveur CoAP sécurisé

Pour exécuter cet exemple, vous devez disposer d’un serveur CoAP sécurisé prenant en charge le mode PSK ou le mode certificat (ou les deux). Vous disposez des options suivantes :

  • Construire et exécuter manuellement un serveur CoAP sécurisé à l’aide, par exemple, de libcoap, Californium, FreeCoAP ou de toute autre bibliothèque CoAP prenant en charge DTLS.
  • Utiliser les images Docker prêtes à l’emploi disponibles sur Docker Hub, qui permettent de créer et d’exécuter les serveurs CoAP sécurisés adaptés à notre exemple. Les étapes nécessaires à l’utilisation des serveurs CoAP basés sur Docker sont décrites ci-dessous.
Configuration d’un serveur en mode PSK

La commande suivante récupère depuis Docker Hub le conteneur Docker d’un serveur CoAP sécurisé basé sur Californium plugtest (qui n’est pas sécurisé par défaut) et le démarre :

docker run --name coap-test-server -d --rm -p 5683:5683/udp -p 5684:5684/udp tqtc/coap-californium-test-server:3.8.0

Le serveur de test CoAP sera accessible sur les ports 5683 (non sécurisé) et 5684 (sécurisé). Pour savoir comment récupérer l’adresse IP, consultez la section « Obtenir l’adresse IP ».

Pour exécuter l’exemple avec ce serveur, vous devez définir la clé pré-partagée sur secretPSK et l’identité sur Client_identity.

Configuration d'un serveur en mode certificat

L'image Docker du serveur sécurisé utilisant l'authentification par certificats X.509 s'appuie sur l'exemple de serveur de temps de la bibliothèque FreeCoAP. La commande suivante récupère le conteneur depuis Docker Hub et le démarre :

docker run --name coap-time-server -d --rm -p 5684:5684/udp tqtc/coap-secure-time-server:freecoap

Pour savoir comment récupérer l’adresse IP, consultez la section « Récupération de l’adresse IP ». Le serveur de test CoAP sera accessible via l’adresse IP récupérée, sur le port 5684 et via le chemin d’accès /time.

Pour exécuter l’exemple avec ce serveur, vous devez spécifier les fichiers de certificats requis par le serveur. Ils se trouvent dans le conteneur Docker, dans le répertoire /root/certs. Pour les copier dans un répertoire local, utilisez la commande suivante :

docker cp <container_id>:/root/certs <local_directory_path>

Par exemple :

$ docker cp 5e46502df88f:/root/certs ~/

Les instructions pour obtenir l’ID du conteneur sont décrites ci-dessous.

Obtenir l’adresse IP

Pour connaître l'adresse IP d'un conteneur Docker, commencez par récupérer l'ID du conteneur en exécutant la commande ` docker ps `, qui affichera un résultat similaire à celui-ci :

$ docker ps
CONTAINER ID        IMAGE
5e46502df88f        tqtc/coap-californium-test-server:3.8.0

Vous pouvez ensuite obtenir l’adresse IP à l’aide de la commande suivante :

docker inspect <container_id> | grep IPAddress

Par exemple :

$ docker inspect 5e46502df88f | grep IPAddress
...
"IPAddress": "172.17.0.2",
...
Arrêt d’un conteneur Docker

Pour arrêter un conteneur Docker après utilisation, utilisez la commande suivante :

docker stop <container_id>

L'<container_id> utilisé ici est le même que celui obtenu par la commande ` docker ps `.

Fichiers :

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.