【Java】 暗号用乱数 ~ SecureRandom ~

■ はじめに

SSL通信では、以下「SSLシーケンス概要」の★部分にあるように乱数を作成してやり取りする。
今回は、Javaでの実装でこの乱数作成について考える

# 本当は、以下のトラブルを解決する過程で、SecureRandom クラスを学ばなければならなかったので
# トラブルシューティングの前段階として、知識を纏めてみた

SSLシーケンス概要

http://itpro.nikkeibp.co.jp/article/COLUMN/20060731/244715/?rt=nocnt
より

               Client                                     Server
               |                                          |
               |  httpsでサーバに対してリクエストを出す   |
                  |----------------------------------------->|
                 |                                          |
               |            サーバ証明書を送付            |
               |<-----------------------------------------|
 証明書をチェック     |                                          |
               |                                          |
 ★乱数作成★       |                                          |
               |                                          |
 サーバ証明書の      |                                          |
 公開鍵で乱数を暗号化  |                                          |
                     |                                          |
               |           暗号化した乱数を送信           |
                  |----------------------------------------->|
乱数を元に共通鍵を作成 |                                          | 乱数を元に共通鍵を作成
               |                                          |
               |  共通暗号鍵方式でデータを暗号化して通信  |
                  |<---------------------------------------->|
               |                                          |
もう少し詳細なシーケンス図は以下の関連記事を参照のこと
http://blogs.yahoo.co.jp/dk521123/34090545.html

■ 暗号用乱数生成方法

[1] SecureRandom.getInstanceStrong() で作成する(From Java1.8)
 => プラットフォームでもっとも強力なアルゴリズムを使用してSecureRandomインスタンスを作成

[2] SecureRandom.getInstance(【アルゴリズム】)で作成する
 => 【アルゴリズム】:"NativePRNGBlocking"/"NativePRNGNonBlocking"/"NativePRNG"/"SHA1PRNG" など
 => OSによって、サポートするアルゴリズムに差異があるので注意
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/SunProviders.html#SecureRandomImp
[3] new SecureRandom()で作成する

[4] 「-Dsecurerandom.source=file:/dev/urandom」or
    「System.setProperty("securerandom.source", "file:/dev/urandom");」
 => 「securerandom.source=file:/dev/random」だと「/dev/random」の性質上、ブロックする可能性があるので注意
http://otndnld.oracle.co.jp/document/products/E13153_01/wlcp/wlss40/configwlss/jvmrand.html
http://qa.atmarkit.co.jp/q/1901

■ どの方法がいい?

ずるい結論かもしれないが、結局は...
 * 開発環境だけでなく、本番を想定した環境で試したうえで決める
  => 一見、「SecureRandom.getInstanceStrong()を指定しとけばいい」って思ったが、
     ハードウェア(PC/Mobile/Raspberry PI etc...)・OS環境・使用する証明書・暗号アルゴリズムなどによっては、
     パフォーマンスに影響するので、しっかり実環境で試した方がいい

選定の仕方

i) まず、Java1.8以上であれば「[1] SecureRandom.getInstanceStrong()」を使って、実環境で確認する
   => 特に問題がなければ、OK!
   => (実際に起こった現象なのだが)SecureRandom.getInstanceStrong()を使用して
      以下の関連記事のようなトラブルがあり、リクエスタイムアウトSSL通信できなかった。
      (必ずしも、SecureRandom.getInstanceStrong()を使えば正解って訳じゃない)
http://blogs.yahoo.co.jp/dk521123/36643583.html
ii) i)で問題がある又はJava1.7以下の環境の場合は、
   「[2] SecureRandom.getInstance(【アルゴリズム】)」で直接アルゴリズムを指定するか
   「[3] new SecureRandom()」を使用し、実環境で確認する

※ 以下のサイトが非常に参考になる
http://qiita.com/aibax/items/29e09318ac85c4e93c1a


関連記事

Webサービス / Metro [8] ~ SSL通信を行う (2) / クライアントサイド ~

http://blogs.yahoo.co.jp/dk521123/36528991.html

WebサービスにアクセスするJavaクライアントで、Exception: Broken pipeが発生する

http://blogs.yahoo.co.jp/dk521123/36643583.html

SSL / TLS ~知識編~

http://blogs.yahoo.co.jp/dk521123/34090545.html