Sur cette page

Quick Secure CoAP Client

Sécuriser le client CoAP et l'utiliser avec une interface utilisateur Qt Quick.

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

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

Remarque : Qt CoAP ne fournit pas d'API QML dans sa version actuelle. Cependant, vous pouvez rendre les classes C++ du module disponibles pour QML comme cela est montré dans l'exemple.

Exécution de l'exemple

Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.

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 par clé pré-partagée (PSK ) ou par certificat. Pour plus d'informations sur la configuration d'un serveur CoAP sécurisé, voir Configuration d'un serveur CoAP sécurisé.

Exposition de 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 enveloppante personnalisée et utilisez les macros d'enregistrement spéciales.

Créez la classe QmlCoapSecureClient en tant que classe enveloppante autour de QCoapClient. Cette classe contient également le mode de sécurité sélectionné et les paramètres de configuration de la 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 en tant que 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 enums qui y sont fournis :

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

Ajustement des fichiers de construction

Pour rendre les types personnalisés disponibles à partir de QML, mettez à jour les fichiers du système de construction en conséquence.

CMake

Pour une compilation basée sur CMake, ajoutez les éléments suivants 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 basée sur 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

Maintenant que les classes C++ sont correctement exposées à QML, vous pouvez utiliser les nouveaux types.

Création du client

CoapSecureClient Le client 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 invoquée à partir du code QML lorsque l'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);
    }
}

Du côté C++, cette méthode crée un QCoapClient et se connecte à ses signaux finished() et error(). La classe gère les 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 définit simplement 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;
}

La surcharge pour les certificats X.509 lit les fichiers de certificat et la clé privée et 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;
}

Après avoir défini la configuration de sécurité, la méthode sendGetRequest définit l'URL de la demande et envoie une demande 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, un échange avec le serveur CoAP est effectué. Une fois la poignée de main réussie, tous les messages suivants sont cryptés, et la modification de 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 annulera la poignée de main et fermera les sockets ouvertes.

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 pour ouvrir une boîte de dialogue de fichier lorsque le bouton est enfoncé :

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 pour 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
}

Mise en place d'un serveur CoAP sécurisé

Pour exécuter cet exemple, vous devez disposer d'un serveur CoAP sécurisé prenant en charge les modes PSK ou Certificat (ou les deux). Vous avez les options suivantes :

  • Construire et exécuter manuellement un serveur CoAP sécurisé en utilisant, par exemple, libcoap, Californium, FreeCoAP, ou toute autre bibliothèque CoAP qui supporte DTLS.
  • Utiliser les images Docker prêtes à l'emploi disponibles sur Docker Hub, qui construisent et exécutent 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 pour le mode PSK

La commande suivante extrait du 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 CoAP test sera accessible sur les ports 5683 (non sécurisé) et 5684 (sécurisé). Pour obtenir des instructions sur la récupération de l'adresse IP, voir 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 avec des certificats X.509 est basée sur l'exemple du serveur de temps de la bibliothèque FreeCoAP. La commande suivante extrait le conteneur de 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 obtenir des instructions sur la récupération de l'adresse IP, voir Obtenir l'adresse IP. Le serveur de test CoAP sera accessible par l'adresse IP récupérée sur le port 5684 et le chemin de ressource /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 obtenir l'adresse IP d'un conteneur Docker, commencez par récupérer l'ID du conteneur en exécutant la commande docker ps, qui produira quelque chose comme :

$ 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",
...
Terminer un conteneur Docker

Pour mettre fin à un conteneur Docker après utilisation, utilisez la commande suivante :

docker stop <container_id>

L'adresse <container_id> est la même que celle récupérée 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.