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 サーバを開発します。着信接続を処理し、QSslSocket インスタンスを提供するために、QSslServer を使用することができます。そのために、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"); else 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
ページが表示される。
©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。