SSLサーバーとクライアント

QSslSocketsを使用したセキュアなリモート・オブジェクト・ネットワークのセットアップ。

通信を暗号化することは、完全に制御できないネットワークを通してデータを渡す必要がある場合に重要です。この例の2つのアプリケーションは、SSL接続を介してリモートオブジェクトを共有する方法と、それらにアクセスする方法を示しています。

sslserverと sslcppclientはどちらもカスタムルートCA証明書を使い、sslserver/certにあるお互いの証明書を検証します。

SSLサーバー

sslserverは証明書と秘密鍵で設定されます。

auto config = QSslConfiguration::defaultConfiguration();
config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
QFile certificateFile(QStringLiteral(":/sslcert/server.crt"));
if (certificateFile.open(QIODevice::ReadOnly | QIODevice::Text))
    config.setLocalCertificate(QSslCertificate(certificateFile.readAll(), QSsl::Pem));
else
    qFatal("Could not open certificate file");
QFile keyFile(QStringLiteral(":/sslcert/server.key"));
if (keyFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
    if (key.isNull())
        qFatal("Key is not valid");
    config.setPrivateKey(key);
} else {
    qFatal("Could not open key file");
}
config.setPeerVerifyMode(QSslSocket::VerifyPeer);
QSslConfiguration::setDefaultConfiguration(config);

そしてQRemoteObjectHost オブジェクトとQSslServer オブジェクトを作成します。QSslServer オブジェクトはポート65511でリッスンします。次に、QRemoteObjectHost オブジェクトにQSslServer オブジェクトの URL を指定して setHostUrl を呼び出す。

QRemoteObjectHost host;
QSslServer server;
server.listen(QHostAddress::Any, 65511);
host.setHostUrl(server.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration);

errorOccurredシグナルを処理するためにラムダが使用され、エラーがターミナルに出力される。2番目のラムダは、エラーハンドラを接続するpendingConnectionAvailableシグナルに接続され、受信ソケットを引数としてQRemoteObjectHost オブジェクト上でaddHostSideConnectionを呼び出し、ホストオブジェクトに通信用のソケットを使用させる。

QObject::connect(&server, &QSslServer::errorOccurred,
                 [](QSslSocket *socket, QAbstractSocket::SocketError error) {
                     Q_UNUSED(socket);
                     qDebug() << "QSslServer::errorOccurred" << error;
                 });
QObject::connect(&server, &QSslServer::pendingConnectionAvailable, [&server, &host]() {
    qDebug() << "New connection available";
    QSslSocket *socket = qobject_cast<QSslSocket *>(server.nextPendingConnection());
    Q_ASSERT(socket);
    QObject::connect(socket, &QSslSocket::errorOccurred,
                     [](QAbstractSocket::SocketError error) {
                         qDebug() << "QSslSocket::error" << error;
                     });
    host.addHostSideConnection(socket);
});

最後に、MinuteTimerオブジェクトを作成し、QRemoteObjectHost オブジェクト上でMinuteTimerオブジェクトを引数としてenableRemotingを呼び出し、共有できるようにする。

MinuteTimer timer;
host.enableRemoting(&timer);

SSLクライアント

sslcppclientはルートCA証明書を設定し、Testerオブジェクトを作成します。

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    auto config = QSslConfiguration::defaultConfiguration();
    config.setCaCertificates(QSslCertificate::fromPath(QStringLiteral(":/sslcert/rootCA.pem")));
    QSslConfiguration::setDefaultConfiguration(config);

    Tester t;
    return a.exec();
}

Tester コンストラクタで、一時的なQRemoteObjectNode オブジェクトが作成され、 setupConnection を使ってQSslSocket オブジェクトを作成し設定します。エラー・ハンドラが接続され、QSslSocket に addClientSideConnection を呼び出すことで、QRemoteObjectNode オブジェクトが使用される。

QRemoteObjectNode m_client;
auto socket = setupConnection();
connect(socket, &QSslSocket::errorOccurred,
        socket, [](QAbstractSocket::SocketError error){
    qDebug() << "QSslSocket::error" << error;
}) ;
m_client.addClientSideConnection(socket);

次に、Testerクラスのメンバである3つのQScopedPointerQRemoteObjectNode オブジェクトでacquireを使用して、MinuteTimerの3つのレプリカに接続される。最後にQTimer::singleShot 、遅延の後にresetを4回呼び出す。

ptr1.reset(m_client.acquire< MinuteTimerReplica >());
ptr2.reset(m_client.acquire< MinuteTimerReplica >());
ptr3.reset(m_client.acquire< MinuteTimerReplica >());
QTimer::singleShot(0, this, &Tester::clear);
QTimer::singleShot(1, this, &Tester::clear);
QTimer::singleShot(10000, this, &Tester::clear);
QTimer::singleShot(11000, this, &Tester::clear);

Tester::clearが最初の3回呼び出されると、1つのポインタがバインドされていることがチェックされ、毎回異なるポインタがリセットされます。4回目に呼び出されると、アプリケーションが終了します。

void clear()
{
    static int i = 0;
    if (i == 0) {
        i++;
        if (ptr1.isNull())
            qCritical() << "Pointer 1 was not set";
        ptr1.reset();
    } else if (i == 1) {
        i++;
        if (ptr2.isNull())
            qCritical() << "Pointer 2 was not set";
        ptr2.reset();
    } else if (i == 2) {
        i++;
        if (ptr3.isNull())
            qCritical() << "Pointer 3 was not set";
        ptr3.reset();
    } else {
        qApp->quit();
    }
}

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。