クイックセキュア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サーバーが必要です。以下のオプションがあります:

  • libcoapCaliforniumFreeCoAP、または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.