Javaで暗号的に強力な乱数が必要な場合は、SecureRandom
を使用します。残念ながら、SecureRandom
は非常に遅い場合があります。 Linuxで/dev/random
を使用する場合、十分なエントロピーが構築されるのを待つことをブロックできます。パフォーマンスの低下をどのように回避しますか?
誰もこの問題の解決策として ncommon Maths を使用しましたか?
このパフォーマンスの問題がJDK 6で解決されたことを誰でも確認できますか?
真のランダムデータが必要な場合は、残念ながらそれを待つ必要があります。これには、SecureRandom
PRNGのシードが含まれます。 Uncommon Mathsは、インターネットに接続して特定のWebサイトからシードデータをダウンロードできますが、SecureRandom
より速く真のランダムデータを収集することはできません。私の推測では、これは/dev/random
が利用可能な場合よりも高速ではないでしょう。
PRNGが必要な場合は、次のようにします。
SecureRandom.getInstance("SHA1PRNG");
サポートされる文字列は、SecureRandom
SPIプロバイダーによって異なりますが、Security.getProviders()
およびProvider.getService()
を使用して列挙できます。
SunはSHA1PRNGを好むため、広く利用可能です。 PRNGの場合、特に高速ではありませんが、PRNGはエントロピーの物理的な測定をブロックするのではなく、単に計算するだけの数値になります。
例外は、データを取得する前にsetSeed()
を呼び出さない場合、next()
またはnextBytes()
を初めて呼び出すと、PRNGがシードされます。通常、システムからのかなり少量の真のランダムデータを使用してこれを行います。この呼び出しはブロックする場合がありますが、乱数のソースは、「PIDと一緒に現在の時刻をハッシュし、27を追加し、最高の結果を期待する」よりもはるかに安全です。ただし、ゲームに必要なのが乱数だけである場合、またはテストの目的で同じシードを使用してストリームを将来繰り返し可能にしたい場合は、安全でないシードが引き続き役立ちます。
Linuxでは、次のコマンドを使用して、より高速だが少し安全性の低い/ dev/urandomを選択できるはずです。
-Djava.security.egd=file:/dev/urandom
ただし、これはJava 5以降では機能しません( Java Bug 6202721 )。推奨される回避策は次を使用することです。
-Djava.security.egd=file:/dev/./urandom
(余分な/./
に注意してください)
Linuxでは、SecureRandom
のデフォルトの実装はNativePRNG
(ソースコード here )であり、非常に遅い傾向があります。 Windowsでは、デフォルトはSHA1PRNG
です。他の人が指摘したように、明示的に指定するとLinuxでも使用できます。
NativePRNG
は、SHA1PRNG
およびUncommons Mathsの AESCounterRNG とは異なり、オペレーティングシステムから継続的にエントロピーを受け取ります(/dev/urandom
から読み取ります)。他のPRNGは、シード後に追加のエントロピーを取得しません。
AESCounterRNGはSHA1PRNG
よりも約10倍高速で、IIRC自体はNativePRNG
よりも2〜3倍高速です。
初期化後にエントロピーを取得するより高速なPRNGが必要な場合は、 Fortuna のJava実装を見つけることができるかどうかを確認してください。 Fortuna実装のコアPRNGはAESCounterRNGで使用されるものと同じですが、エントロピープーリングと自動再シードの洗練されたシステムもあります。
多くのLinuxディストリビューション(ほとんどがDebianベース)は、エントロピーに/dev/random
を使用するようにOpenJDKを構成します。
/dev/random
は定義上低速です(ブロックすることもあります)。
ここから、ブロックを解除する方法に関する2つのオプションがあります。
オプション1、エントロピーの改善
/dev/random
により多くのエントロピーを取得するには、 haveged デーモンを試してください。 HAVEGEエントロピーを継続的に収集するデーモンであり、特別なハードウェアを必要とせず、CPU自体とクロックのみを必要とするため、仮想化環境でも動作します。
Ubuntu/Debianの場合:
apt-get install haveged
update-rc.d haveged defaults
service haveged start
RHEL/CentOSの場合:
yum install haveged
systemctl enable haveged
systemctl start haveged
オプション2.ランダム性の要件を減らします
何らかの理由で上記の解決策が役に立たない場合、または暗号的に強いランダム性を気にしない場合は、代わりに/dev/urandom
に切り替えることができます。これにより、ブロックされないことが保証されます。
グローバルに行うには、デフォルトのJavaインストールでファイルjre/lib/security/Java.security
を編集して/dev/urandom
を使用します(別の bug で指定する必要があります/dev/./urandom
)。
このような:
#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
その後、コマンドラインで指定する必要はありません。
注:暗号化を行う場合、良好なエントロピーが必要です。適切なケース- Android PRNG issue Bitcoinウォレットのセキュリティが低下しました。
ヘッドレスDebianサーバーで一度に約25秒間SecureRandom
ブロッキングを呼び出すと、同様の問題が発生しました。 haveged
デーモンをインストールして、/dev/random
が確実に追加されるようにします。必要なエントロピーを生成するには、このようなものが必要です。 SecureRandom
への私の呼び出しは、おそらくミリ秒かかります。
本当に「暗号的に強い」ランダム性が必要な場合は、強力なエントロピーソースが必要です。 /dev/random
は、システムイベントがエントロピー(ディスク読み取り、ネットワークパケット、マウスの動き、キーの押下など)を収集するまで待機する必要があるため、低速です。
より高速なソリューションは、ハードウェア乱数ジェネレーターです。マザーボードには既に1つが組み込まれている場合があります。 hw_random documentation をチェックして、もしあなたがそれを持っているかどうか、そしてそれをどのように使用するかを判断する手順を調べてください。 rng-toolsパッケージには、ハードウェアで生成されたエントロピーを/dev/random
に供給するデーモンが含まれています。
システムでHRNGを使用できず、パフォーマンスのためにエントロピーの強度を犠牲にする場合は、PRNGに/dev/random
からのデータをシードし、PRNG作業の大部分を行います。 SP800-9 にリストされているいくつかのNIST承認のPRNGがあり、それらは実装が簡単です。
/dev/random
について参照した問題は、SecureRandom
アルゴリズムではなく、それが使用するランダム性のソースにあります。 2つは直交しています。 2つのうちどちらが遅くなっているのかを把握する必要があります。
リンクした珍しい数学のページには、ランダム性の原因に取り組んでいないことが明記されています。
BouncyCastleなどのさまざまなJCEプロバイダーを試して、SecureRandom
の実装が高速かどうかを確認できます。
簡単な search は、デフォルトの実装をFortunaに置き換えるLinuxパッチも明らかにします。これについてはあまり知りませんが、調査してください。
また、実装が不適切なSecureRandom
アルゴリズムやランダム性ソースを使用することは非常に危険ですが、カスタム実装のSecureRandomSpi
を使用して独自のJCEプロバイダーをロールできることにも言及する必要があります。プロバイダに署名させるには、Sunとのプロセスを経る必要がありますが、実際には非常に簡単です。暗号化ライブラリに関する米国の輸出規制を認識していることを示すフォームをFAXで送信するだけです。
システムに人工的なランダム性を与えるツール(少なくともUbuntu上)があります。コマンドは単純です:
rngd -r /dev/urandom
前面にSudoが必要になる場合があります。 rng-toolsパッケージがない場合は、インストールする必要があります。これを試してみましたが、間違いなく助けになりました!
ソース: matt vs world
私は同じ issue に直面しました。適切な検索用語でいくつかのグーグル検索を行った後、 DigitalOcean に関するこの素敵な記事に出会いました。
ここの記事の関連部分を引用するだけです。
HAVEGEの原理に基づいており、以前は関連するライブラリに基づいていましたが、havegedでは、プロセッサでのコード実行時間の変動に基づいてランダム性を生成できます。同じハードウェア上の同じ環境であっても、1つのコードで同じ正確な時間を実行することはほぼ不可能なので、1つまたは複数のプログラムを実行するタイミングは、ランダムソースをシードするのに適しているはずです。 haveged実装は、ループを繰り返し実行した後、プロセッサーのタイムスタンプカウンター(TSC)の違いを使用して、システムのランダムソース(通常は/ dev/random)をシードします。
この記事の手順に従ってください。 https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged
投稿しました こちら
Java 8を使用すると、LinuxでSecureRandom.getInstanceStrong()
を呼び出すとNativePRNGBlocking
アルゴリズムが得られることがわかりました。多くの場合、これは数秒間ブロックして、数バイトのソルトを生成します。
代わりにNativePRNGNonBlocking
を明示的に要求するように切り替えましたが、名前から予想されるとおり、ブロックされなくなりました。これがセキュリティにどのような影響を与えるかはわかりません。おそらく、非ブロッキングバージョンでは、使用されているエントロピーの量を保証することはできません。
Update:OK、わかりました この素晴らしい説明 。
簡単に言うと、ブロックを回避するには、new SecureRandom()
を使用します。これは/dev/urandom
を使用します。これはブロックせず、基本的に/dev/random
と同じくらい安全です。投稿から:「/ dev/randomを呼び出したいのは、マシンが最初に起動し、エントロピーがまだ蓄積されていないときだけです」。
SecureRandom.getInstanceStrong()
は絶対的な最強のRNGを提供しますが、大量のブロッキングが影響しない状況でのみ安全に使用できます。
反復アルゴリズムの初期化ソースとしてセキュアランダムを使用します。 UncommonMathの代わりにMersenneツイスターを一括作業に使用できます。UncommonMathはしばらく使用されており、他のprngよりも優れていることが証明されています
http://en.wikipedia.org/wiki/Mersenne_twister
初期化に使用するセキュアランダムを今すぐ更新してください。たとえば、クライアントごとに1つのmersenne twister擬似ランダムジェネレーターを使用して、クライアントごとに1つのセキュアランダムを生成し、十分な高度のランダム化を取得できます。
私自身はこの問題にぶつかりませんでしたが、プログラムの起動時にスレッドを生成し、すぐにシードを生成しようとしてから死にます。ランダムに呼び出すメソッドは、スレッドが生きていればそのスレッドに参加するので、最初の呼び出しはプログラム実行の非常に早い段階でのみブロックします。
ハードウェアでサポートされている場合は、 Java RdRand Utilityを使用 を試してください。
IntelのRDRAND
命令に基づいており、SecureRandom
の約10倍高速であり、大容量実装の帯域幅の問題はありません。
この実装は、命令を提供するCPUでのみ機能することに注意してください(つまり、rdrand
プロセッサフラグが設定されている場合)。 RdRandRandom()
コンストラクターを使用して明示的にインスタンス化する必要があります。特定のProvider
は実装されていません。
RNGの要件を明確にする必要があるようです。最も強力な暗号化RNG要件(私が理解しているように)は、それらを生成するために使用されるアルゴリズムを知っていて、以前に生成されたすべての乱数を知っていても、で生成された乱数に関する有用な情報を取得できないことです将来、非現実的な量の計算能力を費やすことなく。
このランダム性の完全な保証が必要ない場合は、おそらく適切なパフォーマンスのトレードオフがあります。私は Dan Dyerの反応 Uncommons-MathsのAESCounterRNG、またはFortuna(その著者の1人は暗号の専門家であるBruce Schneierです)に同意する傾向があります。どちらも使用したことはありませんが、アイデアは一見信頼できるようです。
think定期的に(たとえば、1日または1時間に1回など)初期ランダムシードを生成できる場合、高速ストリーム暗号を使用してストリームの連続したチャンクから乱数を生成します(ストリーム暗号がXORを使用する場合、nullのストリームを渡すか、XORビットを直接取得します)。 ECRYPTの eStream プロジェクトには、パフォーマンスベンチマークを含む多くの優れた情報があります。これは、あなたがそれを補充する時点の間のエントロピーを維持しないので、誰かがあなたが乱数とあなたが使用したアルゴリズムの1つを知っていれば、技術的には、多くの計算能力で、ストリーム暗号を破って、内部状態を推測して、将来の乱数を予測できるようにします。しかし、そのリスクとその結果がエントロピーを維持するコストを正当化するのに十分かどうかを判断する必要があります。
編集:ここにいくつかあります RNGの暗号のコースノート このトピックに非常に関連性のある 'ネットで見つけた。
私の経験では、PRNGの初期化が遅いだけで、その後のランダムデータの生成はありません。より熱心な初期化戦略を試してください。作成に費用がかかるため、シングルトンのように扱い、同じインスタンスを再利用します。 1つのインスタンスのスレッド競合が多すぎる場合は、プールするか、スレッドをローカルにします。
乱数生成について妥協しないでください。脆弱性があると、すべてのセキュリティが侵害されます。
COTSの原子崩壊ベースのジェネレーターはあまり見かけませんが、大量のランダムデータが本当に必要な場合は、いくつかの計画があります。 HotBitsなど、常に興味深いものを見ることができるサイトの1つは John Walker's Fourmilab。 です。
他に見るべきものは、ファイルlib/security/Java.securityのプロパティsecurerandom.sourceです。
/ dev/randomではなく/ dev/urandomを使用すると、パフォーマンスが向上する場合があります。乱数の品質が重要な場合、セキュリティを損なう妥協をしないでください。
Apache commons Mathプロジェクトを試すことができます。これには、よく知られたアルゴリズムの実装がいくつかあります。
https://commons.Apache.org/proper/commons-math/userguide/random.html
ただし、パフォーマンスには注意してください。 RandomDataGenerator
のデフォルトコンストラクターは、Well19937c
の専用インスタンスを作成します。これは非常に高価な操作です。
ドキュメントによると、このクラスはスレッドセーフではありませんが、1つのスレッドのみがこのクラスにアクセスすることを保証できる場合は、1つのインスタンスのみを初期化できますスレッドごと。