WebEngine ウィジェット クライアント証明書の例
Qt WebEngine とQSslServer を使用したシンプルなクライアント証明書認証シナリオ。
この例では、クライアント証明書認証のワークフローを示します。この認証シナリオは、例えば、組み込みデバイスに実装することができ、組み込みデバイスは、その機 能を処理するためのウェブインターフェースを提供します。管理者は、組み込みデバイスを保守するためにQt WebEngine を使用し、認証のためにカスタム SSL 証明書を持つ。接続はSSLソケットで暗号化されます。組み込みデバイスは、QSslSocket
を使用して認証と暗号化を処理します。この方法では、管理者は認証情報を入力する必要がなく、デバイスが認識する適切な証明書を選択するだけです。
この例では、ワークフローを示すために、非常にシンプルで最小限のアプローチに焦点を当てている。QSslSocket は、リソースの限られた組み込みデバイス上で本格的な HTTPS サーバーを実行する必要がないため、低レベルのソリューションであることに注意してください。
証明書の作成
この例ではすでに証明書が生成されていますが、新しい証明書を生成する方法を見てみましょう。OpenSSLツールを使ってサーバとクライアントの証明書を作成します。
まず、証明書署名要求CSR
を作成し、署名します。CAの秘密鍵を使って、クライアントとサーバーの両方のローカル証明書に署名し、発行します。
openssl req -out ca.pem -new -x509 -nodes -keyout ca.key
注: -days
オプションを指定して、デフォルトの証明書有効期間である30日をオーバーライドします。
では、クライアントとサーバー用に2つの秘密鍵を作成しましょう:
openssl genrsa -out client.key 2048
openssl genrsa -out server.key 2048
次に、2つの証明書署名要求が必要だ:
openssl req -key client.key -new -out client.req
openssl req -key server.key -new -out server.req
CSRから両方の証明書を発行しよう:
openssl x509 -req -in client.req -out client.pem -CA ca.pem -CAkey ca.key
openssl x509 -req -in server.req -out server.pem -CA ca.pem -CAkey ca.key
クライアント証明書のサブジェクトとシリアル番号は、認証時に選択できるように表示されます。シリアル番号は
openssl x509 -serial -noout -in client.pem
クライアントの実装
さて、ウェブブラウザのクライアントを実装しましょう。
まず、証明書とその秘密鍵をロードし、QSslCertificate とQSslKey インスタンスを作成する。
QFile certFile(":/resources/client.pem"); certFile.open(QIODevice::ReadOnly); const QSslCertificate cert(certFile.readAll(), QSsl::Pem); QFile keyFile(":/resources/client.key"); keyFile.open(QIODevice::ReadOnly); const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "");
次に、証明書とその秘密鍵をQWebEngineClientCertificateStore に追加する。
QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(cert, sslKey);
証明書を処理するには、QWebEnginePage のインスタンスを作成し、QWebEnginePage::certificateError とQWebEnginePage::selectClientCertificate の2つのシングルに接続する必要がある。自己署名サーバー証明書は証明書エラーを引き起こすため、最初の証明書だけが必要である。本番環境では、自己署名証明書は使用されない。したがって、この例では、適切な証明書の提供を避けるために、QWebEngineCertificateError 。秘密鍵は秘密であり、決して公開してはならないことに注意してください。
QWebEnginePage page; QObject::connect(&page, &QWebEnginePage::certificateError, [](QWebEngineCertificateError e) { e.acceptCertificate(); });
QWebEnginePage::selectClientCertificate 、単にQDialog を表示し、QListWidget はクライアント証明書のリストを表示する。ユーザーが選択した証明書は、QWebEngineClientCertificateSelection::select の呼び出しに渡される。
QObject::connect( &page, &QWebEnginePage::selectClientCertificate, &page, [&cert](QWebEngineClientCertificateSelection selection) { QDialog dialog; QVBoxLayout *layout = new QVBoxLayout; QLabel *label = new QLabel(QLatin1String("Select certificate")); QListWidget *listWidget = new QListWidget; listWidget->setSelectionMode(QAbstractItemView::SingleSelection); QPushButton *button = new QPushButton(QLatin1String("Select")); layout->addWidget(label); layout->addWidget(listWidget); layout->addWidget(button); QObject::connect(button, &QPushButton::clicked, [&dialog]() { dialog.accept(); }); const QList<QSslCertificate> &list = selection.certificates(); for (const QSslCertificate &cert : list) { listWidget->addItem(cert.subjectDisplayName() + " : " + cert.serialNumber()); } dialog.setLayout(layout); if (dialog.exec() == QDialog::Accepted) selection.select(list[listWidget->currentRow()]); else selection.selectNone(); });
最後に、QWebEnginePage 用にQWebEngineView を作成し、サーバーの URL をロードして、ページを表示する。
QWebEngineView view(&page); view.setUrl(QUrl("https://localhost:5555")); view.resize(800, 600); view.show();
サーバーの実装
組み込みデバイスのために、最小限のHTTPSサーバーを開発します。QSslServer を使って着信接続を処理し、QSslSocket インスタンスを提供することができる。そのために、QSslServer のインスタンスを作成し、クライアントのセットアップと同様に、サーバー証明書とその秘密鍵をロードする。次に、QSslCertificate とQSslKey オブジェクトを作成する。さらに、サーバーがクライアントから提示された証明書を検証できるように、CA証明書が必要です。CA証明書とローカル証明書はQSslConfiguration にセットされ、後でサーバーによって使用される。
QSslServer server; QSslConfiguration configuration(QSslConfiguration::defaultConfiguration()); configuration.setPeerVerifyMode(QSslSocket::VerifyPeer); QFile keyFile(":/resources/server.key"); keyFile.open(QIODevice::ReadOnly); QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); configuration.setPrivateKey(key); QList<QSslCertificate> localCerts = QSslCertificate::fromPath(":/resources/server.pem"); configuration.setLocalCertificateChain(localCerts); QList<QSslCertificate> caCerts = QSslCertificate::fromPath(":resources/ca.pem"); configuration.addCaCertificates(caCerts); server.setSslConfiguration(configuration);
次に、ポート5555
if(!server.listen(QHostAddress::LocalHost, 5555)) qFatal("Could not start server on localhost:5555"); その他 qInfo("Server started on localhost:5555");
QTcpServer::pendingConnectionAvailable シグナル用にラムダ関数を用意し、着信コネクションの処理を実装する。このシグナルは、認証が成功し、socket
TLS暗号化が開始された後にトリガーされる。
QObject::connect(&server, &QTcpServer::pendingConnectionAvailable, [&server]() { QTcpSocket *socket = server.nextPendingConnection(); Q_ASSERT(socket); QPointer<Request> request(new Request); QObject::connect(socket, &QAbstractSocket::disconnected, socket, [socket, request]() mutable { delete request; socket->deleteLater(); });
Request
QPointer オブジェクトは、 の単純なラッパーである。このオブジェクトは受信HTTPデータを収集する。リクエストが完了するか、ソケットが終了すると削除される。QByteArray
struct Request : public QObject { QByteArray m_data; };
リクエストに対する応答はリクエストされたURLに依存し、HTMLページの形でソケットを通して送り返される。GET
rootリクエストに対して、管理者はAccess Granted
メッセージとExit
HTMLボタンを見る。管理者がそれをクリックすると、クライアントは別の リクエストを送る。今度は/exit
相対URLで、これがサーバー終了のトリガーとなる。
QObject::connect(socket, &QTcpSocket::readyRead, socket, [socket, request]() mutable { request->m_data.append(socket->readAll()); if (!request->m_data.endsWith("\r\n\r\n")) return; socket->write(http_ok); socket->write(html_start); if (request->m_data.startsWith("GET / ")) { socket->write("<p>ACCESS GRANTED !</p>"); socket->write("<p>You reached the place, where no one has gone before.</p>"); socket->write("<button onclick=\"window.location.href='/exit'\">Exit</button>"); } else if (request->m_data.startsWith("GET /exit ")) { socket->write("<p>BYE !</p>"); socket->write("<p>Have good day ...</p>"); QTimer::singleShot(0, &QCoreApplication::quit); } else { socket->write("<p>There is nothing to see here.</p>"); } socket->write(html_end); delete request; socket->disconnectFromHost(); });
server
client
証明書を選択すると、Access Granted
ページが表示される。
© 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.