En esta página

Cliente CoAP de seguridad rápida

Asegurar el cliente CoAP y utilizarlo con una interfaz de usuario Qt Quick.

Cliente CoAP seguro con configuración y respuesta de certificado

Quick Secure CoAP Client muestra cómo crear un cliente CoAP seguro y utilizarlo en una aplicación Qt Quick.

Nota: Qt CoAP no proporciona una API QML en su versión actual. Sin embargo, puedes hacer que las clases C++ del módulo estén disponibles para QML como se muestra en el ejemplo.

Ejecución del ejemplo

Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para obtener más información, consulte Qt Creator: Tutorial: Construir y ejecutar.

Para ejecutar la aplicación de ejemplo, primero debe configurar un servidor CoAP seguro. Puede ejecutar el ejemplo con cualquier servidor CoAP seguro que admita uno de los modos de autenticación de clave precompartida (PSK) o de certificado. Para obtener más información sobre la configuración de un servidor CoAP seguro, consulte Configuración de un servidor CoAP seguro.

Exposición de clases C++ a QML

En este ejemplo, debe exponer la clase QCoapClient y el espacio de nombres QtCoap a QML. Para ello, cree una clase envoltorio personalizada y utilice las macros de registro especiales.

Cree la clase QmlCoapSecureClient como una envoltura alrededor de QCoapClient. Esta clase también contiene el modo de seguridad seleccionado y los parámetros de configuración de seguridad. Utilice la macro Q_INVOKABLE para exponer varios métodos a QML. Utilice también la macro QML_NAMED_ELEMENT para registrar la clase en QML como 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;
};

Después de eso, registra el espacio de nombres QtCoap, para que puedas utilizar los enums proporcionados allí:

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

Ajuste de los archivos de compilación

Para que los tipos personalizados estén disponibles desde QML, actualice los archivos del sistema de compilación en consecuencia.

CMake

Para una compilación basada en CMake, añada lo siguiente a CMakeLists.txt:

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

Para una compilación basada en qmake, modifique el archivo quicksecureclient.pro de la siguiente manera:

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

Uso de nuevos tipos QML

Ahora, cuando las clases C++ estén correctamente expuestas a QML, podrás utilizar los nuevos tipos.

Creación del cliente

CoapSecureClient es instanciado desde el archivo Main.qml. Maneja la señal QmlCoapSecureClient::finished() y actualiza la UI en consecuencia:

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

La instancia de QCoapClient se crea cuando el usuario selecciona o cambia el modo de seguridad en la UI. El método QmlCoapSecureClient::setSecurityMode() se invoca desde el código QML cuando se selecciona uno de los modos de seguridad:

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

En el lado C++, este método crea un QCoapClient y se conecta a sus señales finished() y error(). La clase gestiona internamente ambas señales y las reenvía a la nueva señal 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));
                });
    }
}
Envío de una solicitud

Pulsa el botón Send Request para establecer la configuración de seguridad basada en el modo de seguridad seleccionado y enviar una solicitud 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);
    }
}

Existen dos sobrecargas para el método setSecurityConfiguration.

La sobrecarga para el modo PSK simplemente establece la identidad del cliente y la clave precompartida:

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

Y la sobrecarga para certificados X.509 lee los archivos de certificado y la clave privada y establece la configuración de seguridad:

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

Tras establecer la configuración de seguridad, el método sendGetRequest establece la URL de solicitud y envía una solicitud a 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);
}

Al enviar la primera solicitud, se realiza un "handshake" con el servidor CoAP. Una vez que el handshake se ha realizado con éxito, todos los mensajes subsiguientes están encriptados, y cambiar la configuración de seguridad después de un handshake exitoso no tendrá ningún efecto. Si quieres cambiarla, o cambiar el host, necesitas desconectarte primero.

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

Esto abortará el handshake y cerrará los sockets abiertos.

Para la autenticación mediante certificados X.509, es necesario especificar los archivos de certificado. Para ello se utiliza el componente FilePicker. Combina un campo de texto y un botón para abrir un diálogo de archivo cuando se pulsa el botón:

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 se instancia varias veces en el archivo Main.qml para crear campos de entrada para los certificados y la clave privada:

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
}

Configuración de un servidor CoAP seguro

Para ejecutar este ejemplo, es necesario disponer de un servidor CoAP seguro que admita los modos PSK o Certificado (o ambos). Tiene las siguientes opciones:

  • Construir y ejecutar manualmente un servidor CoAP seguro utilizando, por ejemplo, libcoap, Californium, FreeCoAP, o cualquier otra librería CoAP que soporte DTLS.
  • Utilizar las imágenes Docker listas disponibles en Docker Hub, que construyen y ejecutan los servidores CoAP seguros adecuados para nuestro ejemplo. A continuación se describen los pasos necesarios para utilizar los servidores CoAP basados en Docker.
Configuración de un servidor para el modo PSK

El siguiente comando extrae el contenedor docker para un servidor CoAP seguro basado en Californium plugtest (que no es seguro por defecto) del Docker Hub y lo inicia:

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

El servidor CoAP de prueba será accesible en los puertos 5683 (no seguro) y 5684 (seguro). Para obtener instrucciones sobre cómo recuperar la dirección IP, consulte Obtener la dirección IP.

Para ejecutar el ejemplo con este servidor, debe establecer la clave precompartida en secretPSK y la identidad en Client_identity.

Configuración de un servidor para el modo certificado

La imagen docker del servidor seguro que utiliza autenticación con certificados X.509 está basada en el ejemplo del servidor de tiempo de la librería FreeCoAP. El siguiente comando extrae el contenedor de Docker Hub y lo inicia:

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

Para obtener instrucciones sobre cómo recuperar la dirección IP, consulta Obtener la dirección IP. El servidor de prueba CoAP será accesible por la dirección IP recuperada en el puerto 584 y la ruta de recursos /time.

Para ejecutar el ejemplo con este servidor, es necesario especificar los archivos de certificado requeridos por el servidor. Se encuentran en el contenedor docker, en el directorio /root/certs. Para copiarlos a un directorio local, utilice el siguiente comando:

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

Por ejemplo:

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

Las instrucciones para obtener el ID del contenedor se describen a continuación.

Obtener la dirección IP

Para averiguar la dirección IP de un contenedor docker, primero obtenga el ID del contenedor ejecutando el comando docker ps, que mostrará algo como:

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

A continuación, puede obtener la dirección IP con el siguiente comando:

docker inspect <container_id> | grep IPAddress

Por ejemplo:

$ docker inspect 5e46502df88f | grep IPAddress
...
"IPAddress": "172.17.0.2",
...
Terminar un contenedor Docker

Para terminar un contenedor Docker después de su uso, utilice el siguiente comando:

docker stop <container_id>

El <container_id> aquí es el mismo que el recuperado por el comando docker ps.

Archivos:

© 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.