このページでは

クイック・セキュア CoAP クライアント

CoAPクライアントのセキュリティ対策を施し、Qt Quick のユーザーインターフェースで利用する方法。

証明書の設定と応答を備えたセキュアなCoAPクライアント

「Quick Secure CoAP Client」では、セキュアなCoAPクライアントを作成し、Qt Quick アプリケーションでそれを使用する方法について解説します。

注: Qt CoAP の 現行バージョンでは、QML APIは提供されていません。ただし、この例に示されているように、モジュールのC++クラスをQMLから利用できるようにすることは可能です。

例の動作確認

以下の手順でサンプルを実行できます。

サンプルアプリケーションを実行するには、まずセキュアな 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 は、Main.qml ファイルからインスタンス化されます。これはQmlCoapSecureClient::finished() シグナルを処理し、それに応じて UI を更新します:

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));
                });
    }
}
リクエストの送信

「Send Request 」ボタンをクリックして、選択したセキュリティモードに基づいてセキュリティ設定を行い、「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 メソッドには 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サーバーとのハンドシェイクが行われます。ハンドシェイクが正常に完了すると、それ以降のすべてのメッセージは暗号化され、ハンドシェイク成功後にセキュリティ設定を変更しても何の効果もありません。設定を変更したり、ホストを変更したりしたい場合は、まず接続を切断する必要があります。

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モード用サーバーの設定

次のコマンドを実行すると、Docker Hub からCalifornium plugtest(デフォルトではセキュリティ対応ではありません)をベースにしたセキュアな 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 `、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 テストサーバーには、取得した 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 ` コマンドで取得したIDと同じものです。

ファイル:

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