CLOVER🍀

That was when it all began.

SecureRandomのアルゴリズムなどに関する情報をちゃんと見る

これは、なにをしたくて書いたもの?

Javaの暗号用の乱数ジェネレーター(RNG / Random Number Generator)として、とてもよく使われるSecureRandomですが。

SecureRandom (Java SE 11 & JDK 11 )

ここで指定されるアルゴリズムなどの情報を、あんまりちゃんと見たことがないなぁと思いまして。

1度、調べてみようかと。

ちょっとだけ。

環境

今回の環境は、こちらです。

$ java --version
openjdk 11.0.9.1 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-58-generic #64-Ubuntu SMP Wed Dec 9 08:16:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Java 11、Ubuntu Linux 20.04です。

SecureRandom

SecureRandomのJavadocを見てみると、次のように書いています。

暗号用に強化された乱数ジェネレータ(RNG)を提供する

SecureRandom())

強力な暗号化による乱数は、FIPS 140-2, Security Requirements for Cryptographic Modulesのセクション4.9.1に指定されている統計的乱数生成テストに最低限適合しています。

FIPS 140-2, Security Requirements for Cryptographic Modules

さらに、SecureRandomは、非決定論的な出力を生成する必要があります。 したがって、SecureRandomオブジェクトに渡されるシード材料はすべて予測不可能でなければならず、すべてのSecureRandom出力シーケンスは、「RFC 4086: セキュリティのランダム性要件」で説明されているように、暗号的に強くなければなりません。

Randomness Requirements for Security

また、SecureRandomのインスタンスは、特定の乱数生成アルゴリズムを使用して乱数を生成します。

この乱数生成アルゴリズムですが、いくつかの名前こそ見るものの、その一覧ってどこにあるんだろう?と思っていたら、
こちらもJavadocからたどれますね。

f:id:Kazuhira:20201225003243p:plain

Javaセキュリティ標準アルゴリズム名指定 / SecureRandom乱数生成アルゴリズム

SecureRandomで使えるアルゴリズム

では、SecureRandomで使用できるアルゴリズムをもうちょっと見ていきましょう。

Java 11では、以下の7つのアルゴリズムがあるようです。

アルゴリズム 説明
NativePRNG ネイティブOSから乱数を取得する。乱数生成のブロック性については何も表明されない
NativePRNGBlocking ネイティブOSから乱数を取得し、必要に応じてブロックする
NativePRNGNonBlocking ネイティブOSから乱数を取得するが、アプリケーションの速度低下を避けるためにブロックしない
PKCS11 インストール済および構成済のPKCS#11ライブラリから乱数を取得する
DRBG NIST SP 800-90Ar1で定義されているDRBGメカニズムを使用してSUNプロバイダから提供されたアルゴリズム
SHA1PRNG Sunプロバイダが提供する擬似乱数生成(PRNG)アルゴリズム。 このアルゴリズムは、PRNGの基盤としてSHA-1を使用する
Windows-PRNG Windows OSから乱数を取得する

PRNGは、疑似乱数ジェネレータ(Pseudo Random Number Generator)ですね。

次に、こちらのドキュメントを見ていきます。

Java Platform, Standard Editionセキュリティ開発者ガイド, リリース11

各プラットフォームでどのようなアルゴリズムが使えるのかは、JDKプロバイダ・ドキュメントを見ることになります。

JDKプロバイダ・ドキュメント

SecureRandom実装については、こちら。

SecureRandom実装

SolarisLinuxmacOSWindowsで使えるアルゴリズムが列挙されており、どのアルゴリズムがどのプロバイダーに
含まれているかが記載されています。

たとえば、LinuxだとSUNプロバイダーによるNativePRNG、DRBG、SHA1PRNG、NativePRNGBlocking、NativePRNGNonBlockingが
利用可能です。
デフォルトではSHA1PRNGが選択され、java.securityエントロピー収集デバイスfile:/dev/urandomまたはfile:/dev/random
するとNativePRNGが優先されるようです(と書いているのですが、この後で確認してみたら、もう少し複雑でした)。

SecuraRandomに関するプロバイダーは、SUN、SunPKCS11、SunMSCAPIの3つがあるようです。後者2つは、それぞれSolaris
Windowsで使用されます。

SUNプロバイダ

SunPKCS11プロバイダ

SunMSCAPIプロバイダ

各プロバイダーには、エンジン・クラスと対応するアルゴリズムが含まれています。

エンジン・クラスとは、SecureRandomMessageDigestといったものです。

エンジン・クラスおよび対応するService Provider Interfaceクラス

アルゴリズムNativePRNGSHA-256といったものですね。

プラットフォームで使えるSecureRandomのアルゴリズムとプロパティを確認する

ここで少し、ソースコードを書いてみましょう。

対象のプラットフォームで使えるSecureRandomのアルゴリズムとプロパティを出力してみます。

こんなソースコードを用意。
PrintSecureRandomAlgorithms.java

import java.security.Provider;
import java.security.Security;
import java.util.Map;

public class PrintSecureRandomAlgorithms {
    public static void main(String... args) {
        System.out.println("Platform Supported SecureRandom Algorithms:");
        
        for (String algorithm : Security.getAlgorithms("SecureRandom")) {
            System.out.printf("  %s%n", algorithm);
        }

        System.out.println();

        System.out.println("SecureRandom Properties:");
        for (Provider provider : Security.getProviders()) {
            for (Map.Entry<Object, Object> entry : provider.entrySet()) {
                if (entry.getKey().toString().startsWith("SecureRandom")) {
                    System.out.printf("  [provider: %s] %s = %s%n", provider, entry.getKey(), entry.getValue());
                }
            }
        }
    }
}

使用しているのは、このあたりですね。

Security (Java SE 11 & JDK 11 )

Provider (Java SE 11 & JDK 11 )

ここでのProviderは、先ほど「SUN」や「SunPKCS11」という名前で出てきたプロバイダーのことです。

Security#getProvidersで、使用できるプロバイダーをすべて取得することができます。

実行してみます。

$ java PrintSecureRandomAlgorithms.java 
Platform Supported SecureRandom Algorithms:
  DRBG
  SHA1PRNG
  NATIVEPRNGBLOCKING
  NATIVEPRNGNONBLOCKING
  NATIVEPRNG

SecureRandom Properties:
  [provider: SUN version 11] SecureRandom.NativePRNG ThreadSafe = true
  [provider: SUN version 11] SecureRandom.NativePRNGNonBlocking ThreadSafe = true
  [provider: SUN version 11] SecureRandom.SHA1PRNG = sun.security.provider.SecureRandom
  [provider: SUN version 11] SecureRandom.NativePRNG = sun.security.provider.NativePRNG
  [provider: SUN version 11] SecureRandom.NativePRNGNonBlocking = sun.security.provider.NativePRNG$NonBlocking
  [provider: SUN version 11] SecureRandom.DRBG ImplementedIn = Software
  [provider: SUN version 11] SecureRandom.SHA1PRNG ThreadSafe = true
  [provider: SUN version 11] SecureRandom.SHA1PRNG ImplementedIn = Software
  [provider: SUN version 11] SecureRandom.DRBG = sun.security.provider.DRBG
  [provider: SUN version 11] SecureRandom.NativePRNGBlocking ThreadSafe = true
  [provider: SUN version 11] SecureRandom.DRBG ThreadSafe = true
  [provider: SUN version 11] SecureRandom.NativePRNGBlocking = sun.security.provider.NativePRNG$Blocking

Linux上で動かしているので、使えるアルゴリズムとしてNativePRNG、DRBG、SHA1PRNG、NativePRNGBlocking、
NativePRNGNonBlockingの5つが表示されています。

また、プロパティはSecureRandomのもので絞り込んでいますが、表示されているプロバイダーはSUNだけですね。

SecureRandom内で、どのアルゴリズムが使われる?

次に、SecureRandomのインスタンスで使われるアルゴリズムの決定について見ていきましょう。

SecureRandomのインスタンスを作成するには、コンストラクタを使うかSecureRandom#getInstanceアルゴリズム
指定するか、もしくはSecureRandom#getInstanceStrongで取得することができます。

アルゴリズムを指定した場合はまあいいのですが、その他の方法だとどういうアルゴリズムが選ばれるか気になるところです。

というわけで、こんなソースコードを作って条件を変えながら確認してみます。
PrintSecureRandomInstanceAlgorithm.java

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class PrintSecureRandomInstanceAlgorithm {
    public static void main(String... args) throws NoSuchAlgorithmException {
        SecureRandom secureRandom;

        if (args.length > 0 && "strong".equals(args[0])) {
            secureRandom = SecureRandom.getInstanceStrong();
        } else if (args.length > 0) {
            String algorithm = args[0];
            secureRandom = SecureRandom.getInstance(algorithm);
        } else {
            secureRandom = new SecureRandom();
        }

        System.out.printf("SecureRandom Algorithm = %s%n", secureRandom.getAlgorithm());
    }
}

引数でstrongと指定するとSecureRandom#getInstanceStrongを使い、それ以外の引数を与えるとアルゴリズムを指定した
ことになり、引数なしだとコンストラクタを使ってSecureRandomのインスタンスを生成します。

確認。

## 引数なし
$ java PrintSecureRandomInstanceAlgorithm.java
SecureRandom Algorithm = NativePRNG


## SecureRandom#getInstanceStrong
$ java PrintSecureRandomInstanceAlgorithm.java strong
SecureRandom Algorithm = NativePRNGBlocking


## アルゴリズム指定
$ java PrintSecureRandomInstanceAlgorithm.java DRBG
SecureRandom Algorithm = DRBG

このような結果になりました。

SecureRandomコンストラクタを使う

JDKプロバイダ・ドキュメントによると、Linuxでのデフォルトのアルゴリズム

デフォルトではSHA1PRNGが選択され、java.securityエントロピー収集デバイスfile:/dev/urandomまたはfile:/dev/randomにするとNativePRNGが優先される

ということでした。で、今回はNativePRNGが選択されたことになります。

なので、どこかでjava.securityエントロピー収集デバイスfile:/dev/urandomまたはfile:/dev/randomにしているわけですね。

セキュリティ関連のプロパティというと、以下のどちらかで設定することになります。

セキュリティ・プロパティ

今回はセキュリティ関連のプロパティは、$JAVA_HOME/conf/security/java.securityファイルで見ていくことにしましょう。

$JAVA_HOME/conf/security/java.securityファイル内の、securerandom.sourceを確認してみます。

$ grep securerandom.source /usr/lib/jvm/java-11-openjdk-amd64/conf/security/java.security | grep -v '^#'
securerandom.source=file:/dev/random

/dev/randomですね。

securerandom.sourceコメントアウトしてみましょう。

#securerandom.source=file:/dev/random

確認。

$ java PrintSecureRandomInstanceAlgorithm.java
SecureRandom Algorithm = DRBG

SHA1PRNGになると思っていたのですが、DRBGになりましたね…。どうなっているんでしょう?

ソースコードを見てみます。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/java/security/SecureRandom.java#L270

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/SunEntries.java#L337-L340

今は、/dev/urandomまたは/dev/randomが使えればNativePRNGとなり、そうでなければDRBGという実装みたいです(?)

デフォルトでどうにも決められない場合は、SHA1PRNGが選択されるようです。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/java/security/SecureRandom.java#L284

このあたりの話は、こちらに記載があります。

すべてのJava SE実装は、引数なしのコンストラクタnew SecureRandom()を使用してデフォルトのSecureRandomを提供します。このコンストラクタは、登録されているセキュリティ・プロバイダのリスト内を、最も推奨されるプロバイダから順に確認し、その後、SecureRandom乱数ジェネレータ(RNG)アルゴリズムをサポートする最初のプロバイダから新しいSecureRandomオブジェクトを返します。どのプロバイダもRNGアルゴリズムをサポートしていない場合は、SUNプロバイダからSHA1PRNGを使用するSecureRandomオブジェクトを返します。

SecureRandomオブジェクトの作成

ここで、securerandom.sourceコメントアウトしたまま、システムプロパティjava.security.egd/dev/urandom
指定してみます。

$ java -Djava.security.egd=file:/dev/urandom PrintSecureRandomInstanceAlgorithm.java 
SecureRandom Algorithm = NativePRNG

今度は、NativePRNGになりましたね。

つまり、java.securityエントロピー収集デバイスというのは、次の2つのいずれかで指定するもののようです。

  • システムプロパティjava.security.egd
  • セキュリティ・プロパティ(java.security.Security#setPropertyまたは$JAVA_HOME/conf/security/java.securitysecurerandom.source

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/SunEntries.java#L305-L308

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/SunEntries.java#L325-L333

セキュリティ・プロパティ

SecureRandom#getInstanceStrongを使う

次に、SecureRandom#getInstanceStrongで使われるアルゴリズムについて見ていきましょう。

こちらについては、ドキュメントに記載があります。

java.security.Securityクラスのsecurerandom.strongAlgorithmsプロパティで定義される強力なSecureRandom実装を取得するには、getInstanceStrong()メソッドを使用します。このプロパティは、重要な値を生成するのに適したプラットフォーム実装をリストします。

SecureRandomオブジェクトの作成

$JAVA_HOME/conf/security/java.securitysecurerandom.strongAlgorithmsプロパティで指定されているようです。

確認してみましょう。

$ grep securerandom.strongAlgorithms /usr/lib/jvm/java-11-openjdk-amd64/conf/security/java.security 
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN

アルゴリズム:プロバイダー」の形式で書かれている感じがしますね。また、左から優先でしょうか?
それっぽい感じがします。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/java/security/SecureRandom.java#L929-L965

試してみましょう。左端にNativePRNGを追加。

securerandom.strongAlgorithms=NativePRNG:SUN,NativePRNGBlocking:SUN,DRBG:SUN

確認。

$ java PrintSecureRandomInstanceAlgorithm.java strong
SecureRandom Algorithm = NativePRNG

正解のようですね。

左を、間違ったアルゴリズム名にしてみましょう。

securerandom.strongAlgorithms=foo:SUN,bar:SUN,DRBG:SUN

すると、有効なものが選出されます。

$ java PrintSecureRandomInstanceAlgorithm.java strong
SecureRandom Algorithm = DRBG

なお、全部向こうなアルゴリズムとプロバイダーの組み合わせにすると、例外になります。

Exception in thread "main" java.security.NoSuchAlgorithmException: No strong SecureRandom impls available: xxxxx

SecureRandom#getInstanceStrongを使う場合は、どのようなアルゴリズムが選出されうるか、ちゃんと確認しましょう、
という気分になりますね。

file:/dev/urandomfile:/dev/random

ここまで来ると、file:/dev/urandomfile:/dev/randomの2つがやや気になります。ちょっと調べてみましょう。

RFC 4086にも書いてある、乱数ジェネレーターですね。

/dev/random は、そのプールからのバイト列を返しますが、見積もられるエントロピーがゼロとなるとき、ブロックします。エントロピーが、イベントから、そのプールに追加さえれるに従って、より多くのデータが /dev/random を通じて利用可能になります。このような /dev/random デバイスから得られた乱雑なデータは、十分な乱雑なビット列がそのプール中にある場合、あるいは、合理的な時間が延長されている場合、長期鍵用の鍵生成のために適切です。

/dev/urandom は、/dev/random のように動作します。しかし、これは、たとえ乱雑性のプールについてのエントロピー見積もりがゼロに落ちるときでさえ、データを提供します。これは、セッション鍵生成用、あるいは、より多くの乱雑なビット列を待ち受けるのをブロックすることが許されないような他の鍵生成用に適切である可能性があります。たとえ、そのプールのエントロピーの見積もりが、その過去の出力において小さいときでさえ、データを採り続けることのリスクは、攻撃者が SHA-1 を逆算できるとしたら、現在の出力から計算可能でしょう。SHA-1 が繰り返されないように設計されている場合、これは、合理的なリスクです。

RFC 4086: セキュリティのランダム性要件 / /dev/random デバイス

/dev/random - Wikipedia

ざっくり言うと、/dev/randomは安全性が高いけれどブロックする可能性があり、/dev/urandomは安全性は劣るものの
ブロックしないので性能的には有利、ということみたいです。

ところで、「エントロピー」とは?

情報量 - Wikipedia

源泉については、RFC 4086に記載があります。

RFC 4086: セキュリティのランダム性要件 / エントロピーの源泉

先ほどのRFC 4086の記載の内容からいくと、OS上でのキーボード入力、ディスク関連の割り込み、マウスの動きなどが
源泉となります。ただ、サーバーだとディスク関連の割り込みくらいしか入手できないことになりますが、と。

RFC 4086: セキュリティのランダム性要件 / /dev/random デバイス

SecureRandomのgenerateSeedとnextBytes

JDKプロバイダ・ドキュメントを見ていると、NativePRNGなどにおいて、どのメソッドで/dev/randomが使われるといったことが
書かれています。

SUNプロバイダ

具体的には、SecureRandomのgenerateSeedメソッドとnextBytesメソッドです。

SecureRandom#nextBytesメソッドは、ユーザーが指定したバイト数の乱数バイト配列を生成するものです。

SecureRandomオブジェクトの使用

SecureRandom#generateSeedメソッドは、乱数ジェネレーターにシードを与える場合に使用します。

シード・バイトの生成

ところで、SecureRandom#generateSeedメソッドをふだん使っているかというと、微妙なところだと思います。

SecureRandomクラスのJavadocを見ると、自分でシードに関するメソッドを呼び出さないと、シードされないことが書かれています。

SecureRandom (Java SE 11 & JDK 11 )

こういう情報を踏まえて、SecureRandomの各メソッドとアルゴリズム、乱数ジェネレーターの関係を見ていくと良いのでしょうね。

SecureRandomの各アルゴリズムを少し眺める

最後に、SecureRandomの各アルゴリズムを眺めてみましょう。

まあ、簡単な情報の整理とソースコードの記載だけですが。

アルゴリズムは、SecureRandomSpiクラスのサブクラスとして作成されています。

SecureRandomSpi (Java SE 11 & JDK 11 )

SecureRandomSpiクラスは、SecureRandomクラスが内部で使用しているものです。

各プラットフォームのアルゴリズムの実装は、こちらにあります。

https://github.com/openjdk/jdk11u/tree/jdk-11.0.9+1/src/java.base/share/classes/sun/security/provider

https://github.com/openjdk/jdk11u/tree/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider

https://github.com/openjdk/jdk11u/tree/jdk-11.0.9+1/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11

https://github.com/openjdk/jdk11u/tree/jdk-11.0.9%2B1/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi

NativePRNG、NativePRNGBlocking、NativePRNGNonBlocking

最初は、NativePRNG、NativePRNGBlocking、NativePRNGNonBlockingから。

これらは、ネイティブOSから乱数を得るアルゴリズムということでした。

SecureRandom乱数生成アルゴリズム

ドキュメントを見ると、このように書かれています。

  • NativePRNG
    • nextBytes/dev/urandom
    • generateSeed/dev/random
  • NativePRNGBlocking
    • nextBytes/dev/random
    • generateSeed/dev/random
  • NativePRNGNonBlocking
    • nextBytes/dev/urandom
    • generateSeed/dev/urandom

SUNプロバイダ

NativePRNGBlockingはnextBytesgenerateSeedを問わずブロックする可能性がある/dev/randomを使用し、
NativePRNGNonBlockingはブロックしない/dev/urandomを使います。

NativePRNGはnextBytesはブロックしない/dev/urandomを使い、generateSeedではブロックする可能性がある/dev/random
使用します。

鍵などの用途で使う場合はNativePRNGBlockingを使い、そうでない場合はNativePRNGNonBlockingを使うのが良さそうでしょうか。
nextBytesメソッドのみを使う場合は、NativePRNGとNativePRNGNonBlockingに差はなさそうですね。

この3つは、同じソースコード中に収められています。

NativePRNG。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java

NativePRNGBlocking。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java#L241

NativePRNGNonBlocking。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java#L290

実際に、各乱数ジェネレーター(/dev/randomなど)をどう割り当てているかは、こちらを見ればよいでしょう。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java#L137-L173

なお、NativePRNGのgenerateSeedについてはエントロピー収集デバイスの指定で調整可能なようです。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java#L139-L152

SHA1PRNG

SHA1PRNGの説明はこうでした。

Sunプロバイダが提供する擬似乱数生成(PRNG)アルゴリズム。 このアルゴリズムは、PRNGの基盤としてSHA-1を使用します。 各操作につき値が1増加する64ビット・カウンタを使って鎖状につながった真にランダムなシード値から、SHA-1ハッシュを計算します。 160ビットのSHA-1出力のうち、64ビットだけが使用されます。

SecureRandom乱数生成アルゴリズム

どのプラットフォームでも使えることが特徴です。

実装としては、こちらのようです。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/SecureRandom.java

アルゴリズムを登録しているところ。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/SunEntries.java#L123-L124

ちなみに、SHA1PRNGは暗号学的に安全ではないため、避けた方がよさそうですね。

Google Developers Japan: Android N で廃止されるセキュリティ「Crypto」プロバイダ

NativePRNG系のものが使えないWindowsだと、DRBGを使った方が良いのでは?

DRBG

DRBGは、Java 9から追加されたアルゴリズムです。

JEP 273: DRBG-Based SecureRandom Implementations

ドキュメントによるとこのような説明になっています。

NIST SP 800-90Ar1で定義されているDRBGメカニズムを使用してSUNプロバイダから提供されたアルゴリズム

SecureRandom乱数生成アルゴリズム

NIST Special Publication 800-90A / Recommendation for Random Number Generation Using Deterministic Random Bit Generators

ソースコードはこちら。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/DRBG.java

また、プロバイダーの説明には、以下のように書かれています。

次のメカニズムとアルゴリズムがサポートされています。SHA-224、SHA-512/224、SHA-256、SHA-512/256、SHA-384およびSHA-512を使用するHash_DRBGおよびHMAC_DRBG。AES-128、AES-192およびAES-256を使用するCTR_DRBG (導出関数を使用する場合と使用しない場合がある)。各組合せでサポートされている予測耐性と再シード、およびセキュリティ強度は、112から、それがサポートする最大強度まで要求できます。

SUNプロバイダ

この説明からして、単に「DRBG」と指定する以外にも調整可能な項目がありそうですね。
このあたりの要件に合わせて調整可能なことが、DRBGのポイントのようです。

こちらに、securerandom.drbg.configというプロパティの説明があります。

securerandom.drbg.configは、java.security.Securityクラスのプロパティです。

SecureRandomクラス

というわけで、$JAVA_HOME/conf/security/java.securityファイルを確認してみます。

securerandom.drbg.config=

項目としては存在しますが、値が空ですね。

その上のコメントを見ると、設定方法が書かれています。

# Examples,
#   securerandom.drbg.config=Hash_DRBG,SHA-224,112,none
#   securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df
#
# The default value is an empty string, which is equivalent to
#   securerandom.drbg.config=Hash_DRBG,SHA-256,128,none
#

デフォルトの値は、こちらです、と。

securerandom.drbg.config=Hash_DRBG,SHA-256,128,none

カニズム(Hash_DRBG、CTR_DRBG、Hmac_DRBG)、アルゴリズム(SHA-256など)、強度(112〜)などが指定できます。

構文。

#   aspect:
#     mech_name | algorithm_name | strength | capability | df

カニズム。

#   // The DRBG mechanism to use. Default "Hash_DRBG"
#   mech_name:
#     "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG"

アルゴリズム

#   // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and
#   // HMAC_DRBG, default "SHA-256". The "AES-***" names are for CTR_DRBG,
#   // default "AES-128" when using the limited cryptographic or "AES-256"
#   // when using the unlimited.
#   algorithm_name:
#     "SHA-224" | "SHA-512/224" | "SHA-256" |
#     "SHA-512/256" | "SHA-384" | "SHA-512" |
#     "AES-128" | "AES-192" | "AES-256"

強度。

#   // Security strength requested. Default "128"
#   strength:
#     "112" | "128" | "192" | "256"

予測耐性と再シード。

#   // Prediction resistance and reseeding request. Default "none"
#   //  "pr_and_reseed" - Both prediction resistance and reseeding
#   //                    support requested
#   //  "reseed_only"   - Only reseeding support requested
#   //  "none"          - Neither prediction resistance not reseeding
#   //                    support requested
#   pr:
#     "pr_and_reseed" | "reseed_only" | "none"

カニズムがCTR_DRBGの時に、導出関数を使用するかどうか。

#   // Whether a derivation function should be used. only applicable
#   // to CTR_DRBG. Default "use_df"
#   df:
#     "use_df" | "no_df"

プロパティに指定した項目をパースしているのは、このあたりですね。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/share/classes/sun/security/provider/DRBG.java#L98-L149

Linux環境下では、SecureRandom#getInstanceStrongのデフォルト設定がこうだったこともあり、NativePRNG系のものが
使える状態であれば、そちらでもいいかな?

securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
PKCS11

PKCS11ライブラリより、乱数を取得するアルゴリズムです。

SunPKCS11プロバイダ

ソースコードはこちら。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java

あんまり深追いしません…。

Windows-PRNG

Windows OSから乱数を取得するアルゴリズム、となっていますが…Windows-PRNGというものはなく、実体はSunMSCAPI
だったりします。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/PRNG.java

Windows用のNativePRNGのクラスはありますが、実体がありません。

https://github.com/openjdk/jdk11u/blob/jdk-11.0.9%2B1/src/java.base/unix/classes/sun/security/provider/NativePRNG.java

こちらも、あんまり深追いしません…。

まとめ

これまであまり見てこなかった、SecureRandomのアルゴリズムについて、ちょっと追ってみました。

用語などとちゃんと向き合っていなかったので、かなり苦労しましたが…いろいろ勉強になりましたねぇ。

今度から、しっかり見るようにしましょう…。

参考

JCA (Java 暗号化アーキテクチャ)使い方メモ - Qiita

#JJUG Java における乱数生成器とのつき合い方 - Speaker Deck

Java における乱数生成器とのつき合い方 / #JJUG CCC 2019 fall に登壇してきました - k11i.biz

[java] SecureRandom のアルゴリズムの選択について - tokuhirom's blog