Qt Tech. OpenSSL通信、HTTPS通信を行うプログラムが簡単じゃない!対処方法 新しいページはコチラ
提供: yonewiki
(→概要) |
(→概要) |
||
1行: | 1行: | ||
+ | [[Qt#Qtテクニカルノート|Qtテクニンカルノート]]へ戻る | ||
== '''概要''' == | == '''概要''' == | ||
− | + | OpenSSLは数々の国々で武器輸出管理法の対象になることもあり、拠点を米国に置いていません。そして、Qtはフィンランドエスポーを拠点においていて、主要株主はフィンランドのノキアやデジアとなっています。開発の拠点はノルウェーのオスロという構造から、OpenSSLを抱き合わせる訳にはいかない技術になっています。( ゚д゚)ポカーン ソンナニスゴインダネ。でもQtだから使いやすいハズ。というわけで。 | |
+ | |||
+ | |||
+ | 説明書を読んでHTTP通信のプログラムを勉強しようとしたのですが、手に出来る説明書が古くて、HTTP通信とかやってるんすよ。今どきはhttp://のような通信プロトコルは、あまり使わない。https://これです。暗号化ですね。通信ログっていろいろな部分で確認できるようになっていて、暗号化していないと途中に待ち構えているルーティングの中にいるサーバの主が、悪意を持ってパスワードの入力や個人情報が飛んでくるのを待っていることがあります。これを防ぐのが暗号化です。 | ||
6行: | 10行: | ||
− | 難しいというのは、一般人が自由に保有できるであろう演算装置で計算させても数年以上かかるくらい難しいのだけれど、生成処理は簡単であり、生成処理には法則が唯一に存在するという仕組みにある。例えば因数分解みたいな計算がその一例だ。学校で習った因数分解(例えばx^2+4x+3=(x+1)(x+3))は考えれば解ける程度の問題になっているが、桁数が増えると無理っ!ってなる。じゃコンピュータにやらせてみるか?ってなってプログラムを作ってみたら、a+b=4、axb=3となるようなaとbを求めるというのは桁数が増えるとコンピュータを使ってやっても時間がかかるということがわかってきたりした。そういうたぐいの演算は存在するということがわかったのがこの仕組みの大事なところでもある。(x+1)(x+3)のような分解された値を演算するのはスグだよね。手順通り計算すれば、x^2+4x+3だってわかる。数字だけを扱う因数分解の桁数が少し増えただけでも大変なのに文字列をキーワードにしたら短くまとめたような長さでも相当時間がかかる。例えばx^2+439,193,310x+32,261,087,323,411,425の因数分解の答え<span style="/* 345935895, 93257415 */"></span> | + | 難しいというのは、一般人が自由に保有できるであろう演算装置で計算させても数年以上かかるくらい難しいのだけれど、生成処理は簡単であり、生成処理には法則が唯一に存在するという仕組みにある。例えば因数分解みたいな計算がその一例だ。学校で習った因数分解(例えばx^2+4x+3=(x+1)(x+3))は考えれば解ける程度の問題になっているが、桁数が増えると無理っ!ってなる。じゃコンピュータにやらせてみるか?ってなってプログラムを作ってみたら、a+b=4、axb=3となるようなaとbを求めるというのは桁数が増えるとコンピュータを使ってやっても時間がかかるということがわかってきたりした。そういうたぐいの演算は存在するということがわかったのがこの仕組みの大事なところでもある。(x+1)(x+3)のような分解された値を演算するのはスグだよね。手順通り計算すれば、x^2+4x+3だってわかる。数字だけを扱う因数分解の桁数が少し増えただけでも大変なのに文字列をキーワードにしたら短くまとめたような長さでも相当時間がかかる。例えばx^2+439,193,310x+32,261,087,323,411,425の因数分解の答え<span style="/* 345935895, 93257415 */"></span>は導き出せますか?無理だと思います。求めるプログラムも割かし大きなループが発生します。そんなイメージがいいかなって思う。全貌は掴めなくともイメージは大事です。求めるのが大変な演算が存在する。このイメージができれば十分だと思います。実際の演算アルゴリズムでは単純な因数分解のような演算ではありません。SSLのメカニズムそのものを作りたいという人は、もうちょっと別のsiteを徘徊して、理解するとよいと思います。素因数分解の68,718,821,377 = 131,071 x 524,287も同様に人間にとってもコンピュータにとっても困難な計算です。 |
+ | |||
+ | |||
+ | 簡単には、2つの素数pとq、それからp x qを法(その値に達したところで0に戻る仕組み。除算の余りみたいなもの)とする。pを秘密鍵、qを公開鍵とする。ユーザは法の中で平文の数値コードをq乗したものを送付。サーバはp値を演算においてをp、qそれぞれを1引いた値の最小公倍数+1をrとした場合、r - q +1 の結果のみが、法の中で復元できる暗号データに対する乗数になります。法の中でべき乗の結果が循環するという法則が、最小公倍数を求めるのに必要な、秘密の鍵pを知る側だけに複合できることに繋がっています。 | ||
71行: | 78行: | ||
2020年4月21日のVersion1.1.1.gを使っているようです。 | 2020年4月21日のVersion1.1.1.gを使っているようです。 | ||
+ | |||
+ | |||
+ | ちなみにうまくSSLが使えていない場合は以下のようなエラーが取得できます。 | ||
+ | |||
+ | <syntaxhighlight2 lang="Cpp"> | ||
+ | qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | 上記のメッセージは以下のようなシグナルとスロットの接続があった場合に | ||
+ | |||
+ | <syntaxhighlight2 lang="Cpp"> | ||
+ | … | ||
+ | connect(xxxxxNetAccess, SIGNAL(finished(QNetworkReply*)), | ||
+ | this, SLOT(readXml(QNetworkReply*)));//xxxxxNetAccess is QNetworkAccessManager Object. | ||
+ | … | ||
+ | xxxxxNetAccess->get(QNetworkRequest(QStringLiteral("https://www.yo-net.jp"))); | ||
+ | … | ||
+ | void xxx...xxx::readXml(QNetworkReply* reply){ | ||
+ | qDebug() << reply->error(); | ||
+ | } | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | 上記のように取得できるエラー出力で得られるモノです。処理は以下のような流れです。 | ||
+ | |||
+ | :1.QNetworkAccessManagerクラスのxxxxxNetAccessのシグナルfinished(QNetworkReply*)は状態通知を引数とする通信処理が完了関数が呼び出されたときに、reedXml(QNetworkReply*)関数を呼び出す仕組みにします。 | ||
+ | |||
+ | |||
+ | :2.xxxxxNetAccess->get()メンバ関数でQNetworkrequest(QStringLiteral("https://www.yo-net.jp"))の戻り値を引数にして通信処理を行います。これによりxxxxxNetAccessのfineshed(QNetworkReply*)が呼び出され、readXml関数が実行されます。 | ||
+ | |||
+ | |||
+ | :3.readXmlの引数である通信応答オブジェクトreplyからエラー内容を出力。 | ||
+ | |||
+ | |||
+ | というような流れです。 | ||
+ | |||
+ | |||
+ | これはSSL通信の準備が出来ていない事を示しています。ということですから、SSL設定を何かやらなくてはいけなくて、それをやっていないということらしい。 | ||
+ | |||
+ | |||
+ | そういうことなので、Qtのリファレンスを読んでみました。[https://doc.qt.io/qt-5/windows-requirements.html https://doc.qt.io/qt-5/windows-requirements.html]。するとやっぱり、自分でOpenSSLをセットアップしないとダメなんだそうな。ヘ(゚д゚)ノ ナンジャソリャ? | ||
+ | |||
+ | |||
+ | 上記のリンクの記事の見出し辺りを読むと、スグにオープンソースの本家[https://github.com/openssl/openssl https://github.com/openssl/openssl]のWinows版([https://slproweb.com/products/Win32OpenSSL.html https://slproweb.com/products/Win32OpenSSL.html])を提供しているサイトに出向いて、インストールしたくなりますが、実際はQtの追加コンポーネントとして、最適なOpenSSLが入手できるとなっています。そして、Qtのコンポーネントとして導入するOpenSSLは、最も最適化されているという文献を見つけたりしたのです。 | ||
+ | |||
+ | |||
+ | なので、まずはC:\QtにあるMaintenanceTool.exeを起動して、ログイン情報を入力して、[Next](※SignUpボタンは新規アカウント作成なので押し間違えない。)を押して、add or remove Components を選択して[Next]、追加コンポーネント選択画面でQt>>Developper and Designer Tools>>>>OpenSSL1.1.1j Toolkitを選択する。あとは[Next]を続けざまに押し行くだけです。これで、C:\Qt\Tool\OpenSSLに一式が配置されます。 | ||
+ | |||
+ | |||
+ | ネットの多くの文献によると、プロジェクトファイルのディレクトリに2つのdllを置くべきだと言っている。その二つはssleay32.dll、libeay32.dllだという(以降は二つを合わせて*eay32.dllと表現)。あるいはlibssl-1_1.dll、libcrypto-1_1.dll(以降は二つを合わせて*-1_1.dllと表現)。とは言いつつ*eay32.dllは手元に無い状態です。古いSSLにあるらしい。OpenSSL1.1.1j Toolkitで入手したファイル群のC:\Qt\Tool\OpenSSL\binはlibssl-1_1.dll、libcrypto-1_1.dllの二つしかない。 | ||
+ | |||
+ | |||
+ | *eay32.dllと*1_1.dllの違いは端的に言えば、古いか新しいかの違いがあるらしい。中身が似通っているかっていうとそうでもなないらしい。そして、Qt5.15.2では*1_1.dllが配置されている。なので、使い方があるのかどうかわからないにしても、*1_1.dllをなんとかして使うらしい。 | ||
+ | |||
+ | |||
+ | ここからが難しそうだなと思ったのですが、<span style = "background:linear-gradient(transparent 75%, #ff7f7f 75%); font-weight:bold; ">開発しているプログラムの実行ファイルが配置されるディレクトリと同じディレクトリに*1_1.dllを配置すると有効になりました。</span> | ||
+ | |||
+ | |||
+ | |||
+ | <syntaxhighlight2 lang="cpp"> | ||
+ | qDebug() << QSslSocket::supportsSsl(); | ||
+ | </syntaxhighlight2> | ||
+ | 出力結果 | ||
+ | <syntaxhighlight2 lang="cpp"> | ||
+ | true | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | |||
+ | とくに、SSL通信をするための準備をする必要も無くQNetworkAccessManageクラスだけで通信できるようになります。 | ||
+ | |||
+ | |||
+ | 簡単なプログラムの流れは以下になると思います。 | ||
+ | |||
+ | |||
+ | |||
+ | ===''' 処理の流れ '''=== | ||
+ | <syntaxhighlight2 lang="Cpp"> | ||
+ | void CqApp::Init() //CqApp は QObject を継承したQtの機能的クラスの派生クラス(QSystemTrayIcon,QPlainTextEdit,とか | ||
+ | //QNetworkAccessMangeの機能を使う以上は引数thisポインタが必要なので | ||
+ | //QObjectから派生しているクラスである必要がある。 | ||
+ | { | ||
+ | //ここでのInitメンバ関数は初期化とかコンストラクタのような処理 | ||
+ | |||
+ | qNetworkAcessManager_Data = new QNetworkAccessManager(this); | ||
+ | //QNetworkAccessManagerクラスのオブジェクトを生成します。左辺は自分で決めていいです。任意でどうぞ。 | ||
+ | //QNetworkAccessManager* qNetworkAcessManager_Data;のようなメンバ変数としての宣言が事前に必要です。 | ||
+ | … | ||
+ | |||
+ | connect(qNetworkAcessManager_Data, SIGNAL(finished(QNetworkReply*)), | ||
+ | this, SLOT(readData(QNetworkReply*))); | ||
+ | |||
+ | //こんな感じでQt独特のシグナルとスロットという考え方の関数で | ||
+ | //qNetworkAcessManager_Dataのfinishedという通知(引数は内部的にQNetworkRply*型)を受け取ると | ||
+ | //このクラスのreadDataメンバ関数を(内部的にQNetworkRply*型を引数として)呼び出すことを宣言します。 | ||
+ | //イベント駆動のやり方です。 | ||
+ | |||
+ | qDebug() << QSslSocket::supportsSsl(); | ||
+ | //SSL通信ができるか確認できる。true(=正常) or false(=ちゃんと設定出来ていない)。 | ||
+ | |||
+ | CqApp::request(); | ||
+ | … | ||
+ | } | ||
+ | … | ||
+ | void CqApp::request() | ||
+ | { | ||
+ | … | ||
+ | qNetworkAcessManager_Data->get( QNetworkRequest(QUrl(QStringLiteral("https://xxx.xx/data.xml")))); | ||
+ | //メンバ変数を利用してget関数に指定したアドレスの内容を読み込みます。 | ||
+ | //通信が終わったらシグナルスロットの仕組みによりthis::readData関数が呼び出されます。 | ||
+ | … | ||
+ | } | ||
+ | … | ||
+ | void CqApp::readData(QNetworkReply *qNetworkReply_Reply) | ||
+ | { | ||
+ | … | ||
+ | if(qNetworkReply_Reply->error() != QNetworkReply::NoError) { | ||
+ | // この区間でSSL通信エラーが発生した時の処理 | ||
+ | } | ||
+ | else{ | ||
+ | // この区間で通信成功時の処理 | ||
+ | } | ||
+ | … | ||
+ | } | ||
+ | </syntaxhighlight2> | ||
+ | |||
+ | 大まかな流れは、こんな感じですね。具体的なサンプルじゃなくて申し訳ない。 | ||
+ | |||
+ | |||
+ | |||
+ | ===''' 発展的な内容 '''=== | ||
+ | dll(動的リンク)で動作するのはわかった。じゃぁ静的リンクはどうなんだ?C:\Qt\Tools\OpenSSL\Win_x86\libにlibcrypt.libとlibssl.libの二つのライブラリがありました。これで取り込まれるようにできるのかなって思った。静的リンクに変更できるか確認をしてみたいと思います。もうちょっと時間かかるかもな。管理人にとっては割かし複雑な構造をしていると感じるQt手強い。 | ||
+ | |||
+ | |||
+ | |||
+ | [[Qt#Qtテクニカルノート|Qtテクニンカルノート]]へ戻る |