tokuhirom's Blog

[java] SecureRandom のアルゴリズムの選択について

前提

Java SE 8 + Linux 前提です。

Java で暗号的に安全な乱数をえる

Java で暗号的に安全な乱数を得るには SecureRandom クラスを利用します。 SecureRandom ではいくつかの乱数生成アルゴリズムをサポートしています。

各プラットフォームでもっとも安全な SecureRandom の実装は ${JAVA_HOME}/jre/lib/security/java.security の securerandom.strongAlgorithms という項目に書いてあります。 SecureRandom#getInstanceStrong() で取得できるのはこれです。

インスタンスの取得方法

各アルゴリズムの詳細

実装は以下のようになっています。

SHA1PRNG (Initial seeding is currently done via a combination of system attributes and the java.security entropy gathering device)
NativePRNG (nextBytes() uses /dev/urandom, generateSeed() uses /dev/random)
NativePRNGBlocking (nextBytes() and generateSeed() use /dev/random)
NativePRNGNonBlocking (nextBytes() and generateSeed() use /dev/urandom)

http://docs.oracle.com/javase/jp/8/technotes/guides/security/SunProviders.html

NativePRNGBlocking は /dev/random から取得するので、遅いです。 あまり利用されていない開発サーバーなどでは 30 秒ぐらいかかります。 鍵の生成などにはこれを利用するべきですが、ちょっとしたトークンの生成などに利用すると、めっちゃ遅くてなけます。

トークンの生成等には NativePRNG または NativePRNGNonBlocking を利用すれば十分かと。

ちなみに、NativePRNGBlocking と NativePRNGNonBlocking は最近導入されたようです。

For UNIX-like platforms, two new implementations were introduced which provide blocking and non-blocking behavior: NativePRNGBlocking and NativePRNGNonBlocking. http://docs.oracle.com/javase/8/docs/technotes/guides/security/enhancements-8.html

securerandom.source の変更について

インターネットを見ていると、securerandom.source を変更して /dev/urandom を見るようにしろ、という記述を見かけますが、これは Java 8 の世界では間違ったやり方だと私は思います。

NativePRNGNonBlocking を利用するように実装を変更するのが正しいアプローチだと私は考えます。

tips

/dev/random がどんどん出てくるかどうかは head -n 1 /dev/random で調べることができる。

まとめ

とりあえず弱いよりは強い方がいいだろってことで SecureRandom#getInstanceStrong() を呼んだりしてるとサービスが刺さる。

参考文献

2016-05-12 追記

デフォルトの SecureRandom の Algorithm を確認する方法

import java.security.SecureRandom;

public class Hoge {
        public static void main(String[] args) {
                SecureRandom sr = new SecureRandom();
                System.out.println(sr.getAlgorithm());
        }
}