快速安全CoAP客户端
确保CoAP客户端的安全并使用Qt Quick用户界面。
快速安全CoAP客户端展示了如何创建一个安全的CoAP客户端并在Qt Quick应用程序中使用它。
注意:当前版本的Qt CoAP不提供QML API。但是,您可以根据示例将其模块的C++类提供给QML。
运行示例
要从Qt Creator中运行示例,请打开欢迎模式并从示例中选择示例。有关更多信息,请访问构建和运行示例。
要运行示例应用程序,您需要首先设置一个安全的CoAP服务器。您可以使用支持以下任一预共享密钥(PSK)或证书认证模式的任何安全CoAP服务器来运行示例。有关设置安全CoAP服务器的更多信息,请参阅设置安全CoAP服务器。
将C++类公开给QML
在此示例中,您需要将QCoapClient类和QtCoap命名空间公开给QML。要实现此目的,请创建一个自定义包装类并使用特殊的注册宏。
创建QmlCoapSecureClient
类作为QCoapClient的包装器。此类还包含所选的安全模式和安全性配置参数。使用Q_INVOKABLE宏将几个方法公开给QML。还使用QML_NAMED_ELEMENT宏将类在QML中注册为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; };
之后,注册QtCoap命名空间,因此您可以使用那里提供的枚举值。
namespace QCoapForeignNamespace { Q_NAMESPACE QML_FOREIGN_NAMESPACE(QtCoap) QML_NAMED_ELEMENT(QtCoap) }
调整构建文件
要使自定义类型从QML可用,请相应地更新构建系统文件。
CMake
对于基于CMake的构建,将以下内容添加到CMakeLists.txt
中:
qt_add_qml_module(quicksecureclient URI CoapSecureClientModule SOURCES qmlcoapsecureclient.cpp qmlcoapsecureclient.h QML_FILES FilePicker.qml Main.qml )
qmake
对于qmake构建,以下方式修改quicksecureclient.pro
文件:
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
使用新的QML类型
现在,当C++类正确公开给QML时,您可以使用新类型。
创建客户端
CoapSecureClient
对象从 Main.qml
文件实例化。它处理 QmlCoapSecureClient::finished()
信号并根据情况更新 UI
CoapSecureClient { id: client onFinished: (result) => { outputView.text = result; statusLabel.text = ""; disconnectButton.enabled = true; } }
当用户在 UI 中选择或更改安全模式时,会创建 QCoapClient 实例。当选中某个安全模式时,从 QML 代码调用 QmlCoapSecureClient::setSecurityMode()
方法
ButtonGroup { id: securityModeGroup onClicked: { if ((securityModeGroup.checkedButton as RadioButton) === preSharedMode) client.setSecurityMode(QtCoap.SecurityMode.PreSharedKey); else client.setSecurityMode(QtCoap.SecurityMode.Certificate); } }
在 C++ 一侧,此方法创建一个 QCoapClient 并连接到其 finished() 和 error() 信号。该类内部处理这两个信号,并将它们转发到新的 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)); }); } }
发送请求
单击 发送请求 按钮根据所选安全模式设置安全配置并发送 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); } }
对于 setSecurityConfiguration
方法有两种重载方式。
PSK 模式重载仅设置客户端标识和预共享密钥
void QmlCoapSecureClient::setSecurityConfiguration(const QString &preSharedKey, const QString &identity) { QCoapSecurityConfiguration configuration; configuration.setPreSharedKey(preSharedKey.toUtf8()); configuration.setPreSharedKeyIdentity(identity.toUtf8()); m_configuration = configuration; }
而 X.509 证书的重载读取证书文件和私钥,并设置安全配置
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; }
设置安全配置后,sendGetRequest
方法则设置请求 URL 并发送 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); }
在发送首次请求时,会与 CoAP 服务器进行握手。握手成功完成后,所有后续消息都会进行加密。成功握手后更改安全配置不会产生任何效果。如果需要更改,或更改主机,您需要先断开连接。
void QmlCoapSecureClient::disconnect() { if (m_coapClient) m_coapClient->disconnect(); }
这将终止握手并关闭打开的套接字。
对于使用 X.509 证书的认证,需要指定证书文件。《code translate="no">FilePicker 组件用于此目的。它结合了一个文本框和一个按钮,当按钮被按下时,用于打开文件对话框
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
在 Main.qml
文件中多次实例化,用于创建证书和私钥的输入字段
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 }
设置安全的 CoAP 服务器
要运行此示例,您需要一个支持 PSK 或证书模式(或两者)的安全 CoAP 服务器。您有以下选项
- 使用 libcoap、Californium、FreeCoAP 或任何其他支持 DTLS 的 CoAP 库手动构建并运行一个安全的 CoAP 服务器
- 使用 Docker Hub 上可用的现成 Docker 镜像来构建和运行适合我们示例的安全 CoAP 服务器。以下步骤描述了如何使用基于 Docker 的 CoAP 服务器
为 PSK 模式设置服务器
以下命令从 Docker Hub 拉取基于 Californium plugtest(默认为非安全模式)的 secure CoAP 服务器 Docker 容器并启动它
docker run --name coap-test-server -d --rm -p 5683:5683/udp -p 5684:5684/udp tqtc/coap-californium-test-server:3.8.0
CoAP 测试服务器将可通过端口 5683(非安全)和 5684(安全)访问。有关检索 IP 地址的说明,请参阅 获取 IP 地址。
使用此服务器运行示例时,您需要将预共享密钥设置为 secretPSK
,并将标识设置为 Client_identity
。
为证书模式设置服务器
使用 X.509 证书进行认证的 secure 服务器 Docker 镜像是基于 FreeCoAP 库中的 time server 示例。以下命令从 Docker Hub 拉取容器并启动它
docker run --name coap-time-server -d --rm -p 5684:5684/udp tqtc/coap-secure-time-server:freecoap
有关获取IP地址的说明,请参阅获取IP地址。CoAP测试服务器可通过获取的IP地址在端口5684和资源路径/time
上访问。
要使用此服务器运行示例,您需要指定服务器所需的证书文件。它们位于docker容器中的/root/certs
目录下。要将它们复制到本地目录,请使用以下命令
docker cp <container_id>:/root/certs <local_directory_path>
例如:
$ docker cp 5e46502df88f:/root/certs ~/
获取容器ID的说明如下。
获取IP地址
要查找docker容器的IP地址,首先通过运行docker ps
命令获取容器ID,将输出类似的信息
$ docker ps CONTAINER ID IMAGE 5e46502df88f tqtc/coap-californium-test-server:3.8.0
然后,您可以使用以下命令获取IP地址
docker inspect <container_id> | grep IPAddress
例如:
$ docker inspect 5e46502df88f | grep IPAddress ... "IPAddress": "172.17.0.2", ...
终止Docker容器
要终止使用后的docker容器,请使用以下命令
docker stop <container_id>
这里的<container_id>
与通过docker ps
命令获取的相同。
文件
© 2024 Qt公司有限公司。此处包含的文档贡献是各自所有者的版权。此处提供的文档是根据自由软件基金会发布并许可的GNU自由文档许可协议版本1.3的条款许可的。Qt和相应的标志是芬兰以及/或其他国家的Qt公司有限公司的商标。所有其他商标均为各自所有者所有。