クイックセキュアCoAPクライアント
CoAPクライアントを保護し、Qt Quick ユーザー・インターフェースで使用する。
Quick Secure CoAP Clientでは、安全な CoAP クライアントを作成し、Qt Quick アプリケーションで使用する方法を紹介しています。
注: Qt CoAP は、現在のバージョンでは QML API を提供していません。しかし、この例で示したように、モジュールの C++ クラスを QML で利用できるようにすることは可能です。
例の実行
例題を実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳しくは、Building and Running an Example を参照してください。
サンプル・アプリケーションを実行するには、まず安全な CoAP サーバーをセットアップする必要があります。事前共有鍵(PSK)または証明書認証モードのいずれかをサポートするセキュアな CoAP サーバーであれば、サンプルを実行できます。セキュアな CoAP サーバーのセットアップの詳細については、「セキュアな CoAP サーバーのセットアップ」を参照してください。
C++ クラスの QML への公開
この例では、QCoapClient クラスとQtCoap 名前空間を QML に公開する必要があります。そのためには、カスタムラッパークラスを作成し、特別な登録マクロを使用します。
QCoapClient のラッパーとしてQmlCoapSecureClient
クラスを作成します。このクラスは選択されたセキュリティモードとセキュリティ設定パラメータも保持します。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
は ファイルからインスタンス化されます。 シグナルを処理し、それに応じて UI を更新します:Main.qml
QmlCoapSecureClient::finished()
CoapSecureClient { id: client onFinished: (result) => { outputView.text = result; statusLabel.text = ""; disconnectButton.enabled = true; } }
QCoapClient のインスタンスは、ユーザーがUIでセキュリティモードを選択または変更したときに生成されます。セキュリティモードが選択されると、QmlCoapSecureClient::setSecurityMode()
メソッドが QML コードから呼び出されます:
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
リクエストを送信するには、Send Request ボタンをクリックします:
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
メソッドには2つのオーバーロードがあります。
PSKモード用のオーバーロードは、クライアントのIDと事前共有鍵 を設定するだけである:
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サーバーとのハンドシェイクが実行される。最初のリクエストを送信するとき、CoAPサーバーとのハンドシェイクが行われる。ハンドシェイクが成功すると、それ以降のメッセージはすべて暗号化され、ハンドシェイク成功後にセキュリティ・コンフィギュレーションを変更しても影響はない。変更したい場合やホストを変更したい場合は、まず切断する必要がある。
void QmlCoapSecureClient::disconnect() { if (m_coapClient) m_coapClient->disconnect(); }
これによりハンドシェイクが中止され、開いているソケットが閉じられる。
X.509証明書を使用した認証では、証明書ファイルを指定する必要がある。このために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モード用サーバーのセットアップ
以下のコマンドは、Californium plugtest(デフォルトではセキュアではない)をベースにしたセキュアなCoAPサーバー用のdockerコンテナをDocker Hubから取り出し、起動する:
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
に、ID をClient_identity
に設定する必要がある。
証明書モード用サーバーのセットアップ
X.509 証明書による認証を使用するセキュアサーバーの docker イメージは、FreeCoAP ライブラリのタイムサーバーの例に基づいています。以下のコマンドで Docker Hub からコンテナを取り出し、起動する:
docker run --name coap-time-server -d --rm -p 5684:5684/udp tqtc/coap-secure-time-server:freecoap
IPアドレスの取得方法については、IPアドレスの取得を参照してください。CoAPテストサーバーは、ポート5684とリソースパス/time
で、取得したIPアドレスで到達可能になります。
このサーバーでサンプルを実行するには、サーバーが必要とする証明書ファイルを指定する必要がある。これらは 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 stop <container_id>
ここでの<container_id>
は、docker ps
コマンドで取得したものと同じである。
ファイル:
© 2025 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.