Skip to content

Instantly share code, notes, and snippets.

@buzztaiki
Last active May 13, 2021 05:27
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save buzztaiki/ca89a079b093d716ef94 to your computer and use it in GitHub Desktop.
Save buzztaiki/ca89a079b093d716ef94 to your computer and use it in GitHub Desktop.
SecureRandomでブロックしない乱数生成器を使うメモ

JEP-123 で利用時に指定する方法が提案されてJava8で使えるようになったらしい。Linuxだと標準で /dev/random を使うからエントロピーが全部消費されてる状態だとブロックされてた。

Link

Java8

SecureRandom を作るときに NativePRNGNonBlocking を使う

SecureRandom.getInstance("NativePRNGNonBlocking")

Java8から強い乱数生成器を使うには SecureRandom.getInstanceStrong() が推奨されてるっぽい。

System.out.println(new SecureRandom().getAlgorithm());
System.out.println(SecureRandom.getInstanceStrong().getAlgorithm());
NativePRNG
NativePRNGBlocking

Java7

http://stackoverflow.com/questions/137212/how-to-solve-performance-problem-with-java-securerandom

a. java.security.egd に urandom を指定する

java -Djava.security.egd=file:/dev/./urandom

b. SecureRandom を作るときに SHA1PRNG を使う (Windows の場合と同じ動作)

SecureRandom.getInstance("SHA1PRNG")

というか、1.7.0_71 で java.security に urandom 指定されてるし random 指定すると SHA1PRNG が使われるんですが??

ブロックと乱数生成器について

いろいろ勘違いしてた。

  • Java7の場合
    • securerandom.source のデフォルト値は /dev/urandom になっている
    • securerandom.source/dev/urandom が指定されているとデフォルトのアルゴリズムとして NativePRNG が使われる
      • 乱数生成器は /dev/urandom が使われる
    • securerandom.source/dev/random が指定されているとデフォルトのアルゴリズムとして SHA1PRNG が使われる
      • 乱数生成器はJavaで作られたものを使う
      • この時乱数の種を java.security.egd で指定されたデバイスから取得する
        • 乱数の種はおそらくJavaの起動時に一度だけ取得される (もしくは SecureRandom を作った時)
        • java.security.egd のデフォルトは /dev/random になっているから、何度も実行するとブロックすることがある
        • java.security.egd/dev/urandom を指定すると種も擬似乱数になるからブロックしない
  • Java8
    • securerandom.source のデフォルト値は /dev/random になっている
    • securerandom.source/dev/random でも /dev/urandom でもデフォルトのアルゴリズムとして NativePRNG が使われる
      • 乱数生成器は /dev/random な気がするけど自身ない
    • getInstanceStrong を指定した場合は NativePRNGBlocking がアルゴリズムとして使われる
      • 乱数生成器は /dev/random が使われる

ソース見て確認しよう。

import java.security.SecureRandom;
printRand(new SecureRandom());
printRand(SecureRandom.getInstanceStrong());
printRand(SecureRandom.getInstance("NativePRNG"));
printRand(SecureRandom.getInstance("NativePRNGBlocking"));
printRand(SecureRandom.getInstance("NativePRNGNonBlocking"));
printRand(SecureRandom.getInstance("SHA1PRNG"));
def printRand(rand) {
printf("algo=%s, provider=%s, spi=%s", rand.algorithm, type(rand.provider), type(rand.secureRandomSpi));
println()
}
def type(obj) {
obj.getClass().name
}
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
public class UseSecureRandom {
private final ExecutorService pool;
private final int size;
private final int count;
private final long timeout;
public UseSecureRandom(ExecutorService pool, int size, int count, long timeout) {
this.pool = pool;
this.size = size;
this.count = count;
this.timeout = timeout;
}
public static void main(String[] args) throws Exception {
UseSecureRandom usr = new UseSecureRandom(
Executors.newSingleThreadExecutor(daemonThreadFactory()),
65536, 1024,
TimeUnit.SECONDS.toMillis(10));
// System.setProperty("java.security.egd", "file:/dev/urandom");
System.out.println("securerandom.source=" + Security.getProperty("securerandom.source"));
if (System.getProperty("java.version").startsWith("1.7")) {
java7(usr);
} else {
java8(usr);
}
}
static void java8(UseSecureRandom usr) throws Exception {
usr.run(new SecureRandom());
usr.run(SecureRandom.getInstanceStrong());
usr.run(SecureRandom.getInstance("NativePRNGBlocking"));
usr.run(SecureRandom.getInstance("NativePRNGNonBlocking"));
usr.run(SecureRandom.getInstance("SHA1PRNG"));
}
static void java7(UseSecureRandom usr) throws Exception {
usr.run(new SecureRandom());
usr.run(SecureRandom.getInstance("SHA1PRNG"));
}
void run(SecureRandom rand) throws Exception {
int succeed = 0;
int cancelled = 0;
for (Future<byte[]> n : pool.invokeAll(tasks(rand), timeout, TimeUnit.MILLISECONDS)) {
if (!n.isCancelled()) {
succeed++;
} else {
cancelled++;
}
}
System.out.format("%s: succeed=%d, cancelled=%d%n", rand.getAlgorithm(), succeed, cancelled);
}
List<Callable<byte[]>> tasks(SecureRandom rand) {
List<Callable<byte[]>> tasks = new ArrayList<>();
for (int i = 0; i < count; i++) {
tasks.add(randomGenerator(rand, size));
}
return tasks;
}
static Callable<byte[]> randomGenerator(final SecureRandom rand, final int size) {
return new Callable<byte[]>() {
@Override public byte[] call(){
byte[] bytes = new byte[size];
rand.nextBytes(bytes);
return bytes;
}
};
}
static ThreadFactory daemonThreadFactory() {
final ThreadFactory factory = Executors.defaultThreadFactory();
return new ThreadFactory() {
@Override public Thread newThread(Runnable r) {
Thread th = factory.newThread(r);
th.setDaemon(true);
return th;
}
};
}
}
@buzztaiki
Copy link
Author

ざっくりソースを追った。とりあえず、SHA1PRNG が種に /dev/random を使って NativePRNG は /dev/urandom なのは正しい。

手元のメモには書いたけど後でまとめよう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment