とりあえず使う
まずはとりあえずクライアントとサーバーを作る。 OpenSSL というとオレオレ認証局を作るのがめんどいという感じだけど、 Debian だと
# apt-get install ssl-cert # make-ssl-cert generate-default-snakeoil
こんだけで /etc/ssl/certs/ssl-cert-snakeoil.pem と /etc/ssl/private/ssl-cert-snakeoil.key ができる。ちゃんと作るなら /usr/share/ssl-cert/ssleay.cnf を編集して使えばいいんだけど、これもまぁすごい簡単で、いずれにせよずいぶん楽にできるんだなぁと思った(なんか昔 expect とかで作ってたなー)。
でまぁそれでできたファイルを /tmp の下に置いたとして、サーバ例 in Ruby:
#!/usr/bin/env ruby require 'openssl' require 'socket' include OpenSSL srv = TCPServer.new(4444) if ARGV[0] ctx = SSL::SSLContext.new(ARGV[0]) ctx.cert = X509::Certificate.new(File.read('/tmp/ssl-cert-snakeoil.pem')) ctx.key = PKey::RSA.new(File.read('/tmp/ssl-cert-snakeoil.key')) srv = SSL::SSLServer.new(srv, ctx) end while s = srv.accept cont = s.gets s.print(cont) s.close end
引数で SSLv23|SSLv2|SSLv3|TLSv1 のどれかを指定すると SSL なサーバになって、つけないと普通の TCP サーバになる。 SSLContext の引数は省略すると SSLv23 。
クライアント例 in Ruby:
#!/usr/bin/env ruby require 'openssl' require 'socket' include OpenSSL s = TCPSocket.new('localhost', 4444) if ARGV[0] ctx = SSL::SSLContext.new(ARGV[0]) ctx.ca_file = '/tmp/ssl-cert-snakeoil.pem' ctx.verify_mode = SSL::VERIFY_PEER s = SSL::SSLSocket.new(s, ctx) s.connect end s.puts("hogehoge") puts s.read s.close
同じく引数で通信方式を指定している。 ctx 作って ca_file と verify_mode に値代入するのはやらんでも動くけど、せっかくなんでやった方がいいんじゃないかと思われるたぶん。
通信方式
SSL っていうとなにやら色々通信方式がある。 SSLv2 ってのは古い形式で、なにやらセキュリティホールが見つかってるとか書いてあった。 SSLv3 は新しいヤツでそれほぼ同じ形で標準化したのが TLSv1 。実際 OpenSSL は SSLv3 と TLSv1 は同じ扱いみたい。 SSLv23 ってのはどっちも使えるよ、という話みたい。
SSL はいくつかのプロトコルの集合体(鍵共有、認証、暗号化、ダイジェスト、かな)だけど、そのそれぞれのプロトコルで使うアルゴリズムは色々差し替えられるので結構なパターン数がある。 openssl の cipher オプションで使える暗号化形式一覧がわかる。
% openssl ciphers -v ALL@STRENGTH ADH-AES256-SHA SSLv3 Kx=DH Au=None Enc=AES(256) Mac=SHA1 DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 ...
とかすると使える暗号化形式を強度順で一覧できる。強度つーてもどういう理由で決めてるのかは知らんけど。で、プロトコルが四つあるから暗号名が四つ並ぶわけだけど、なんか RSA とかは省略されるとかまぁなんかよくわからんので、右っかわが大事。略語はそれぞれ Kx = key exchange, Au = authentication, Enc = Encryption, Mac = Mac digest とかみたい。
それぞれ
逆順で書いていく。以下特にあんまり調べてない。 Wikipedia に全部載ってるから正しく/詳しく知るならそっちをどうぞ。
ダイジェストってのは、通信する時にハッシュ値みたいなのを送っておいてデータが壊れてないか確認する。たぶん改竄とかデータおかしくなった時(TCPで止まる気もするけど)対策。 SHA1 か MD5 の二択みたい。
本文の暗号化は共有鍵暗号でやる。共有鍵暗号っていうのは一番簡単なのは、「データは XOR 27 かけて読んで下さいねー」と取り決めしておくような暗号。 XOR かけるだけとかだと出現頻度とかで一瞬で破られるので色々工夫があるらしい。 OpenSSL だと、 AES, 3DES, DES, DES, RC4, RC2 あたりが名前として見える。 RC2 と RC4 は秘密だったけど匿名でバラした子がいて公開情報になったらしい。 DES は古いらしくて、それを改訂しようと NIST とかいうのが標準化したのが AES 。 DES を 3 回するから安全らしいのが 3DES 。よくわからんけど AES でいいんじゃね、という感想を持ちました。
このへんの AES 策定の時の話は何回見ても面白い。
http://h2np.net/bit/aes-rep.html
http://h2np.net/bit/aes2/index.html
鍵共有と認証は公開鍵暗号が必要な部分。鍵共有は共有鍵暗号の鍵を交換するのに公開鍵暗号を使う。全部公開鍵でやっても良さそうに思うけどたぶん公開鍵暗号は遅いからとかじゃないかな。認証は公開鍵暗号でデジタル署名ができるからべりさいんとかの認証局に適当に認めてもらったら身元を証明できるとかなんとか(説明になってない)。
鍵共有は RSA と DH ってのが主にあるみたいで、 RSA は素因数分解とたぶん同じ感じの問題だろうと考えられてる RSA 問題っていうのの不可逆性を利用してうんたら(たぶん)。 DH ってのは Diffie-Hellman の略らしく、離散対数問題の不可逆性を利用してうんたら。違いは DH の方がわかりやすくて RSA はフェルマーの小定理とかが登場して少し難しいこと。だから個人的には離散対数の方が好きです。なんか見た目が綺麗で感心した記憶があるし。だからどうした。
あと離散対数問題を実際にどう暗号に使うかってのは herumi さんが書いておられてわかりやすかった気がします。
http://homepage1.nifty.com/herumi/crypt/crypt01.html#QUIZ
認証は RSA と DSS の二択で DSS の方が離散対数の方…かと思ったんだけど DSS は当初は DSA のことだったんだけど、今は DSS = DSA + RSA + ECDSA のことらしい。
速度 (レイテンシ)
openssl speed とかいうのがあるみたいだけどよくわからんので自分で測定して感覚を掴んでみたいと思った。
でとりあえず localhost と 1000 回くらい hogehoge とか echo サーバーに送って返事を受信するだけの簡単なベンチをしてみて、結果は以下みたいな感じだったんだけど、
0.771862 TCPSocket 6.825988 ["DES-CBC-MD5", "SSLv2", 56, 56] 6.852499 ["EXP-RC4-MD5", "SSLv2", 40, 128] 6.896539 ["EXP-RC2-CBC-MD5", "SSLv2", 40, 128] 7.110548 ["RC4-MD5", "SSLv2", 128, 128] 7.592685 ["RC2-CBC-MD5", "SSLv2", 128, 128] 8.015529 ["RC4-MD5", "TLSv1/SSLv3", 128, 128] 8.417717 ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56] 8.424664 ["AES256-SHA", "TLSv1/SSLv3", 256, 256] 8.443360 ["DES-CBC3-MD5", "SSLv2", 168, 168] 8.534704 ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168] 8.663834 ["AES128-SHA", "TLSv1/SSLv3", 128, 128] 8.993231 ["RC4-SHA", "TLSv1/SSLv3", 128, 128] 16.143871 ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56] 51.640470 ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168] 51.647685 ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56] 52.671211 ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128] 57.752796 ["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256]
なんか色々よくわからんことがあって、まずいくつか例外が飛んじゃって選べないのがあった(たぶんRSAの証明書渡してるからDSAな認証ができないとかだと思う)。あとなんか SSLv3 と TLSv1 はまぁ似たような結果だったのは良かったんだけど、 SSLv3 のヤツを SSLv23 でやると SSLv23 でやった方が速くなったりして謎だなーと。まぁよくわからんけど上の結果はとりあえず、速いのと遅いのがあるなーというくらいの認識でいいんじゃないかと思った。もちろん遅いと言ってもカスみたいなサイズのデータを送るだけの例で遅いだけなので、もっとたくさん送ったら速いのかもしれないし、暗号の強度が強かったりするのかもしれないし、まぁよく知らんです。でもまぁさっきの強さ順のヤツと見比べてみると、こういう細かいの送る用途だと、速い方のグループにいる AES256-SHA あたりがいいんじゃないかとか思ったけど知らんです。
で、 localhost とのやりとりだと現実的じゃないので実際どんくらいの overhead なんかなーと、 shinh.org と実家で 10 往復くらいさせてみたベンチ。
0.911451 TCPSocket 1.433355 ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168] 1.435176 ["AES256-SHA", "TLSv1/SSLv3", 256, 256] 1.514165 ["RC4-SHA", "TLSv1/SSLv3", 128, 128] 1.533170 ["RC4-MD5", "TLSv1/SSLv3", 128, 128] 1.574204 ["AES128-SHA", "TLSv1/SSLv3", 128, 128] 1.596982 ["EXP-RC4-MD5", "SSLv2", 40, 128] 1.600494 ["DES-CBC-MD5", "SSLv2", 56, 56] 1.617288 ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56] 1.626607 ["RC2-CBC-MD5", "SSLv2", 128, 128] 1.634951 ["DES-CBC3-MD5", "SSLv2", 168, 168] 1.708413 ["EXP-RC2-CBC-MD5", "SSLv2", 40, 128] 1.720872 ["RC4-MD5", "SSLv2", 128, 128] 1.863379 ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168] 1.985764 ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56] 2.014566 ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56] 2.108500 ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128] 2.123336 ["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256]
なんか要するに大差ないということがよくわかるのであった。まぁ相変わらず AES256-SHA でいいんちゃうのって感じですが。こっちは試行回数少ないからさっきのベンチよりさらにあてにならない。
速度 (スループット)
shinh.org に対して TCP, SSLv2, SSLv3 のそれぞれで "hogehoge"*10000 というすごく圧縮しやすそうな 80kB の文字列を送ってみた。結果は TCP (0.86sec), SSLv2(1.00sec), SSLv3(0.25sec) ってとこだった。 SSLv2 って圧縮しないんかな。
次に適当な text file (34kB) だと、 TCP(0.36), SSLv2(0.53), SSLv3(0.35) だった。
次に適当な jpeg image (27kb) だと、 TCP(0.33), SSLv2(0.45), SSLv3(0.45) って感じ。
まぁなんか「SSL は圧縮かかるから生 TCP より速かったりする」とたまに聞くのが本当なのかが知りたかっただけなので、 SSLv3 に関しては本当、 SSLv2 は圧縮せんのかな?というあたりがわかったのでまぁ良いとする。
openssl コマンドでクラサバ作り
忘れてた openssl コマンド。アホみたいな数のコマンドがあるけど、とりあえずクラサバ用のコマンドとして s_client と s_server がある。
% openssl s_client -connect www.google.com:443 ... GET / ...
とかそんな感じ。暗号方式指定するなら -tls1 とか。あと -CAfile /tmp/ssl-cert-snakeoil.pem とかつけると認証局の確認ができる。
あとは s_server の方はサーバーを作れて、
% openssl s_server -cert /tmp/ssl-cert-snakeoil.pem -key /tmp/ssl-cert-snakeoil.key
とかで手動で応答を返すサーバを作ることができる。タイプ速度とかに自信がある人は手動検索エンジンとか作るといいんじゃないかと思う。