Java SE 8 + Linux 前提です。
Java で暗号的に安全な乱数を得るには SecureRandom クラスを利用します。 SecureRandom ではいくつかの乱数生成アルゴリズムをサポートしています。
各プラットフォームでもっとも安全な SecureRandom の実装は ${JAVA_HOME}/jre/lib/security/java.security
の securerandom.strongAlgorithms という項目に書いてあります。 SecureRandom#getInstanceStrong()
で取得できるのはこれです。
new SecureRandom()
でデフォルト実装が得られますSecureRandom#getInstance(name)
で名前を指定して実装を得られます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
を変更して /dev/urandom を見るようにしろ、という記述を見かけますが、これは Java 8 の世界では間違ったやり方だと私は思います。
NativePRNGNonBlocking を利用するように実装を変更するのが正しいアプローチだと私は考えます。
/dev/random
がどんどん出てくるかどうかは head -n 1 /dev/random
で調べることができる。
とりあえず弱いよりは強い方がいいだろってことで SecureRandom#getInstanceStrong()
を呼んだりしてるとサービスが刺さる。
import java.security.SecureRandom;
public class Hoge {
public static void main(String[] args) {
SecureRandom sr = new SecureRandom();
System.out.println(sr.getAlgorithm());
}
}