MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - パケットのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - パケット
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == <br><br> == AESを使用した暗号化 == 以下の例では、<code>QAESEncryption</code>クラスを使用して、AES(CBCモード)暗号化を指定して、パケットデータを暗号化している。<br> また、パケット長を計算して、ネットワークバイトオーダーに変換している。<br> 最後に、<code>QTcpSocket::write</code>メソッドを使用して、パケットを送信している。<br> <syntaxhighlight lang="c++"> #include <QtNetwork> #include <QDataStream> #include <QCryptographicHash> #include <QIODevice> // パケットデータをAES暗号化 QByteArray encryptPacket(const QByteArray &data, const QByteArray &key, const QByteArray &iv) { // AES(CBCモード)の指定 QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::CBC); encryption.setKey(key); encryption.setIv(iv); // データを暗号化 QByteArray encrypted = encryption.encode(data); return encrypted; } // パケットの送信 void sendPacket(QTcpSocket *socket, const QByteArray &data, const QByteArray &key, const QByteArray &iv) { // パケットデータをAES暗号化 QByteArray encryptedData = encryptPacket(data, key, iv); // パケット長の計算 quint16 packetLength = encryptedData.size(); // パケット長をネットワークバイトオーダーへ変換 QByteArray packetLengthBytes; QDataStream lengthStream(&packetLengthBytes, QIODevice::WriteOnly); lengthStream.setByteOrder(QDataStream::BigEndian); lengthStream << packetLength; // パケットの送信 socket->write(packetLengthBytes); socket->write(encryptedData); } int main() { QTcpSocket socket; // 接続処理 QByteArray key = "your_key"; // 鍵 QByteArray iv = "your_iv"; // 初期化ベクトル QByteArray packetData = "your_data"; // 送信するパケット sendPacket(&socket, packetData, key, iv); } </syntaxhighlight> <br> 以下の例では、<code>QAESEncryption</code>クラスを使用して、AES(CBCモード)復号を指定して、パケットを復号している。<br> まず、パケット長を受信して、次に、パケットを受信してAES復号する。<br> <syntaxhighlight lang="c++"> #include <QtNetwork> #include <QDataStream> #include <QCryptographicHash> #include <QIODevice> // パケットをAES復号 QByteArray decryptPacket(const QByteArray &data, const QByteArray &key, const QByteArray &iv) { // AES(CBCモード)復号を指定 QAESEncryption decryption(QAESEncryption::AES_128, QAESEncryption::CBC); decryption.setKey(key); decryption.setIv(iv); // データを復号 QByteArray decrypted = decryption.decode(data); return decrypted; } // パケットの受信 QByteArray receivePacket(QTcpSocket *socket, const QByteArray &key, const QByteArray &iv) { QByteArray packetData; qint64 packetLength = -1; // パケット長の受信 while(socket->bytesAvailable() < (qint64)sizeof(packetLength)) { if(!socket->waitForReadyRead()) { return QByteArray(); } } QDataStream lengthStream(socket); lengthStream >> packetLength; // パケットの受信 while (socket->bytesAvailable() < packetLength) { if(!socket->waitForReadyRead()) { return QByteArray(); } } packetData = socket->read(packetLength); // パケットのAES復号 QByteArray decryptedData = decryptPacket(packetData, key, iv); return decryptedData; } int main() { QTcpSocket socket; // 接続処理 QByteArray key = "your_key"; // 鍵 QByteArray iv = "your_iv"; // 初期化ベクトル QByteArray receivedPacket = receivePacket(&socket, key, iv); } </syntaxhighlight> <br><br> == SSLを使用した暗号化 == ==== 暗号化 ==== <code>QSslSocket</code>クラスは、暗号化されたデータの送信に使用できる安全で暗号化されたTCP接続を確立する。<br> クライアントモードとサーバーモードの両方で動作し、TLS 1.3を含む最新のTLSプロトコルをサポートしている。<br> <br> デフォルトでは、<code>QSslSocket</code>クラスは、安全であると考えられているTLSプロトコル(<code>QSsl::SecureProtocols</code>)のみを使用するが、<br> ハンドシェイクが始まる前に行う限り、<code>setProtocol</code>メソッドを呼び出してTLSプロトコルを変更することが可能である。<br> <br> SSL暗号化は、ソケットが<code>ConnectedState</code>になった後、既存のTCPストリームの上で動作する。<br> <br> <code>QSslSocket</code>クラスを使用して安全な接続を確立するには、以下に示す2つの方法がある。<br> * 即座にSSLハンドシェイクを行う方法 * 非暗号化モードで接続が確立された後、遅延SSLハンドシェイクを行う方法 <br> <code>QSslSocket</code>クラスの最も一般的な使用方法は、<code>QSslSocket</code>クラスのインスタンスを生成して、<code>connectToHostEncrypted</code>メソッドを呼び出して、安全な接続を開始する方法である。<br> <code>connectToHostEncrypted</code>メソッドは、接続が確立されると即座にSSLハンドシェイクを開始する。<br> <syntaxhighlight lang="c++"> auto pSocket = std::make_unique<QSslSocket>(this); connect(pSocket, &QSslSocket::encrypted, this, &CSampleClass::ready); pSocket->connectToHostEncrypted("example.com", 993); </syntaxhighlight> <br> プレーンな<code>QTcpSocket</code>クラスと同様、<code>QSslSocket</code>クラスは<code>HostLookupState</code> -> <code>ConnectingState</code>になり、接続が成功すると<code>ConnectedState</code>になる。<br> その後、自動的にハンドシェイクが開始されて、成功するとソケットが暗号化状態になり、使用可能な状態になったことを示す<code>encrypted</code>シグナルが送信される。<br> <u>この時、<code>connectToHostEncrypted</code>メソッドのリターン直後(<code>encrypted</code>シグナルが送信される前)に、ソケットにデータを書き込むことができることに注意する。</u><br> <br> データは、<code>encrypted</code>シグナルが送信されるまで、<code>QSslSocket</code>クラスにキューイングされる。<br< <br> 既存の接続を保護するために遅延SSLハンドシェイクを使用する例として、SSLサーバが着信接続を保護する場合がある。<br> <br> 以下の例のように、<code>QTcpServer</code>クラスを継承して、SslServerクラスを定義したとする。<br> まず、<code>QSslSocket</code>クラスのインスタンスを生成した後、<code>setSocketDescriptor</code>メソッドを呼んで、新しいソケットのディスクリプタを渡された既存のものに設定する。<br> そして、<code>QTcpServer::incomingConnection</code>メソッドをオーバーライドして、そのメソッド内で<code>startServerEncryption</code>メソッドを呼び出すことにより、SSLハンドシェイクを開始する。<br> <syntaxhighlight lang="c++"> void SslServer::incomingConnection(qintptr socketDescriptor) { auto pServerSocket = std::make_unique<QSslSocket>(); if(pServerSocket->setSocketDescriptor(socketDescriptor)) { addPendingConnection(pServerSocket); connect(pServerSocket, &QSslSocket::encrypted, this, &SslServer::ready); pServerSocket->startServerEncryption(); } } </syntaxhighlight> <br> エラーが発生した場合、<code>QSslSocket</code>クラスは<code>sslErrors</code>シグナルを送信する。<br> この時、エラーを無視するためのアクションが取られない場合は、接続は切断される。<br> <br> エラーが発生しても接続を継続する場合は、エラー発生後にこのスロット内、または、<code>QSslSocket</code>クラスのインスタンス生成後、接続が試行される前であれば<code>ignoreSslErrors</code>メソッドを呼び出すことができる。<br> これは、<code>QSslSocket</code>クラスが相手のIDを確立する時に遭遇したエラーを無視することができる。<br> <br> 安全な接続は成功したハンドシェイクで確立されるべきであるため、SSLハンドシェイク中のエラーを無視することは慎重になるべきである。<br> <br> 1度暗号化されると、<code>QSslSocket</code>クラスは通常の<code>QTcpSocket</code>クラスとして使用される。<br> <code>readyRead</code>シグナルが送信される時、<code>read</code>メソッド、<code>canReadLine</code>メソッド、<code>readLine</code>メソッド、<code>getChar</code>メソッドを使用して<code>QSslSocket</code>クラスの内部バッファから復号したデータを読み、<br> <code>write</code>メソッド、<code>putChar</code>メソッドを使用して相手にデータを書き戻すことができる。<br> <br> <code>QSslSocket</code>クラスは、書き込まれたデータを自動的に暗号化して、データがピアに書き込まれると<code>encryptedBytesWritten</code>シグナルを送信する。<br> <br> 便利なことに、<code>QSslSocket</code>クラスは、<code>QTcpSocket</code>クラスのブロッキングメソッドである以下に示すメソッドが存在する。<br> * <code>waitForConnected</code>メソッド * <code>waitForReadyRead</code>メソッド * <code>waitForBytesWritten</code>メソッド * <code>waitForDisconnected</code>メソッド <br> また、暗号化された接続が確立されるまで呼び出し側のスレッドをブロックする<code>waitForEncrypted</code>メソッドも提供されている。<br> <br> <syntaxhighlight lang="c++"> QSslSocket socket; socket.connectToHostEncrypted("http.example.com", 443); if(!socket.waitForEncrypted()) { qDebug() << socket.errorString(); return false; } socket.write("GET / HTTP/1.0\r\n\r\n"); while(socket.waitForReadyRead()) { qDebug() << socket.readAll().data(); } </syntaxhighlight> <br> <code>QSslSocket</code>クラスは、暗号、秘密鍵、ローカル証明書、ピア証明書、認証局(CA)証明書を扱うための広範で使い勝手の良いAPIを提供する。<br> また、ハンドシェイクフェーズで発生したエラーを処理するためのAPIも提供している。<br> <br> また、以下の機能をカスタマイズすることも可能である。<br> * <code>QSslConfiguration::setCiphers</code>メソッド、および、<code>QSslConfiguration::setDefaultCiphers</code>メソッドにより、ハンドシェイク前にソケットの暗号化スイートをカスタマイズすることができる。 * ソケットのローカル証明書と秘密鍵は、ハンドシェイクフェーズの前に<code>setLocalCertificate</code>メソッドと<code>setPrivateKey</code>メソッドを使用して、カスタマイズすることができる。 * <code>QSslConfiguration::addCaCertificate</code>メソッド、<code>QSslConfiguration::addCaCertificates</code>メソッドを使用することにより、CA証明書データベースを拡張し、カスタマイズすることができる。 <br> SSLハンドシェイク中にSSLソケットで使用されるデフォルトCA証明書のリストを拡張するには、デフォルトの設定を更新する必要がある。<br> <syntaxhighlight lang="c++"> QList<QSslCertificate> certificates = getCertificates(); QSslConfiguration configuration = QSslConfiguration::defaultConfiguration(); configuration.addCaCertificates(certificates); QSslConfiguration::setDefaultConfiguration(configuration); </syntaxhighlight> <br> ==== 自己署名証明書を使用する場合 ==== 自己署名証明書と秘密鍵を作成する。<br> openssl req -x509 -sha256 -newkey rsa:3072 \ -subj "/C=JP/ST=Tokyo/L=Tokyo City/O=Company Name/OU=Department/CN=localhost" \ -keyout sshconfig.key -out sshconfig.pem -days 3650 -nodes <br> ==== 自己署名証明書をインストールする場合 ==== 自己署名証明書を信頼済みのルート証明書としてインストールする。<br> <br> まず、自己署名証明書と秘密鍵を作成する。<br> openssl req -x509 -sha256 -newkey rsa:3072 \ -subj "/C=JP/ST=Tokyo/L=Tokyo City/O=Company Name/OU=Department/CN=localhost" \ -keyout sshconfig.key -out sshconfig.pem -days 3650 -nodes <br> 次に、自己署名証明書を、以下に示すディレクトリに配置する。<br> * /etc/pki/trust/anchors *: 当ディレクトリに配置する場合は、サブディレクトリを作成してもよい。 * /usr/share/pki/trust/anchors *: 当ディレクトリに配置する場合は、サブディレクトリは作成しない。 sudo cp sshconfig.pem /etc/pki/trust/anchors # または sudo cp sshconfig.pem /usr/share/pki/trust/anchors <br> 最後に、証明書を信頼済みルート証明書に追加する。<br> sudo update-ca-certificates <br> 自己署名証明書をアンインストールする。<br> sudo rm /etc/pki/trust/anchors/sshconfig.pem sudo update-ca-certificates <br> ==== サンプルコード ==== 以下の例では、OpenSSLライブラリと<code>QTcpSocket</code>クラスを使用して、パケットを暗号化して送信している。<br> <syntaxhighlight lang="c++"> #include <QTcpSocket> #include <QSslSocket> #include <QSslKey> #include <QSslCertificate> #include <QFile> // 暗号化 void encryptPacket(QByteArray &packet) { // OpenSSLライブラリの初期化 SSL_load_error_strings(); SSL_library_init(); // 公開鍵と秘密鍵を読み込む QSslKey privateKey("server.key", QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); QSslCertificate publicKey("server.crt", QSsl::Pem); // QSslSocketオブジェクトの生成 QSslSocket socket; socket.setPrivateKey(privateKey); socket.setLocalCertificate(publicKey); socket.connectToHostEncrypted("example.com", 443); // パケットの暗号化 packet = socket.encrypt(packet); } int main() { // パケットの生成 QByteArray packet = "Hello, world!"; // パケットの暗号化 encryptPacket(packet); // 暗号化されたパケットの送信 QTcpSocket socket; socket.connectToHost("example.com", 1234); // ホスト名とTCPポート番号の指定 socket.write(packet); // 送信が完了するまで待機 socket.waitForBytesWritten(); return 0; } </syntaxhighlight> <br> 以下の例では、暗号化されたパケットを送信するために、TCPソケットとパケットデータを引数として受け取り、OpenSSLライブラリを使用してSSLセッションを確立して、パケットを暗号化して送信する。<br> SSLセッションは、<code>QSslSocket</code>クラスを使用して確立される。<br> <br> この時、事前に、サーバの証明書、クライアントの秘密鍵、サーバの公開鍵をファイルから読み込む必要がある。<br> <syntaxhighlight lang="c++"> #include <QTcpSocket> #include <QSslSocket> #include <QSslKey> #include <QSslCertificate> #include <QFile> void sendEncryptedPacket(QTcpSocket *socket, const QByteArray &packet) { // OpenSSLライブラリの初期化 SSL_load_error_strings(); SSL_library_init(); // SSLコンテキストを生成 SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_client_method()); // サーバの証明書を読み込む QFile certFile("server.crt"); certFile.open(QIODevice::ReadOnly); QSslCertificate cert(certFile.readAll()); certFile.close(); SSL_CTX_use_certificate(ctx, cert.handle()); // クライアントの秘密鍵を読み込む QFile keyFile("client.key"); keyFile.open(QIODevice::ReadOnly); QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "my passphrase"); keyFile.close(); SSL_CTX_use_PrivateKey(ctx, key.handle()); // サーバの公開鍵を読み込む QFile pubkeyFile("server.pub"); pubkeyFile.open(QIODevice::ReadOnly); QSslCertificate pubkey(pubkeyFile.readAll()); pubkeyFile.close(); SSL_CTX_add_extra_chain_cert(ctx, pubkey.handle()); // SSLセッションの確立 QSslSocket sslSocket; sslSocket.setSocketDescriptor(socket->socketDescriptor()); sslSocket.setProtocol(QSsl::TlsV1_2); sslSocket.setLocalCertificate(cert); sslSocket.setPrivateKey(key); sslSocket.addCaCertificate(pubkey); sslSocket.startClientEncryption(); // パケットの暗号化 QByteArray encryptedPacket = sslSocket.encrypt(packet); // 暗号化されたパケットの送信 socket->write(encryptedPacket); socket->flush(); socket->waitForBytesWritten(); // SSLセッションの終了 sslSocket.close(); } </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - パケット
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse