Java で暗号的に強力な乱数が必要な場合は、 を使用しますSecureRandom
。残念ながら、SecureRandom
非常に遅くなる可能性があります。Linuxで使用/dev/random
する場合、十分なエントロピーが構築されるのを待つことをブロックできます。パフォーマンスの低下をどのように回避しますか?
この問題の解決策としてUncommon Mathsを使用した人はいますか?
このパフォーマンスの問題がJDK 6で解決されたことを確認できる人はいますか?
Java で暗号的に強力な乱数が必要な場合は、 を使用しますSecureRandom
。残念ながら、SecureRandom
非常に遅くなる可能性があります。Linuxで使用/dev/random
する場合、十分なエントロピーが構築されるのを待つことをブロックできます。パフォーマンスの低下をどのように回避しますか?
この問題の解決策としてUncommon Mathsを使用した人はいますか?
このパフォーマンスの問題がJDK 6で解決されたことを確認できる人はいますか?
Linuxでは、次のコマンドを使用して、より高速ですが少し安全性の低い/ dev/urandomを選択できるはずです。
-Djava.security.egd=file:/dev/urandom
ただし、これはJava 5以降では機能しません(Javaバグ6202721)。推奨される回避策は、次を使用することです。
-Djava.security.egd=file:/dev/./urandom
(余分なことに注意してください/./
)
真のランダム データが必要な場合は、残念ながらそれを待つ必要があります。SecureRandom
これには、 PRNGのシードが含まれます。SecureRandom
Uncommon Maths は、インターネットに接続して特定の Web サイトからシード データをダウンロードすることはできますが、真のランダム データを より速く収集することはできません。私の推測では、これが/dev/random
利用可能な場所よりも高速になる可能性は低いと思います。
PRNG が必要な場合は、次のようにします。
SecureRandom.getInstance("SHA1PRNG");
サポートされている文字列はSecureRandom
SPI プロバイダーによって異なりますが、 および を使用してそれらを列挙できSecurity.getProviders()
ますProvider.getService()
。
Sun は SHA1PRNG が好きなので、広く利用できます。PRNGほど高速ではありませんが、PRNGは数値を処理するだけで、エントロピーの物理的な測定をブロックしません.
例外は、データを取得する前に呼び出しを行わない場合、最初にorsetSeed()
を呼び出したときに PRNG が自分自身をシードすることです。これは通常、システムからのかなり少量の真のランダム データを使用して行われます。この呼び出しはブロックされる可能性がありますが、乱数のソースは、「現在の時刻を PID と一緒にハッシュし、27 を加算し、最善を尽くす」という方法よりもはるかに安全になります。ただし、必要なのはゲームの乱数だけである場合、またはテスト目的で同じシードを使用して将来ストリームを繰り返し可能にしたい場合は、安全でないシードが引き続き役立ちます。next()
nextBytes()
Linux では、 のデフォルトの実装SecureRandom
はNativePRNG
(ソース コードはこちら) で、非常に遅くなる傾向があります。Windowsでは、デフォルトは ですSHA1PRNG
。他の人が指摘したように、明示的に指定すればLinuxでも使用できます。
NativePRNG
SHA1PRNG
Uncommons Maths のAESCounterRNGとは異なり、オペレーティング システムからエントロピーを (から読み取ることによって/dev/urandom
) 継続的に受け取ります。他の PRNG は、シード後に追加のエントロピーを取得しません。
AESCounterRNG は より約 10 倍速くSHA1PRNG
、IIRC 自体は より 2 倍または 3 倍高速ですNativePRNG
。
初期化後にエントロピーを取得するより高速な PRNG が必要な場合は、 Fortunaの Java 実装が見つかるかどうかを確認してください。Fortuna 実装のコア PRNG は AESCounterRNG で使用されるものと同じですが、エントロピー プーリングと自動再シードの洗練されたシステムもあります。
本当に「暗号的に強い」ランダム性が必要な場合は、強力なエントロピー ソースが必要です。/dev/random
システム イベント (ディスクの読み取り、ネットワーク パケット、マウスの動き、キーの押下など) がエントロピーを収集するのを待つ必要があるため、低速です。
より高速なソリューションは、ハードウェア乱数ジェネレーターです。マザーボードに既に 1 つ組み込まれている場合があります。hw_randomのドキュメントを参照して、持っているかどうかの確認方法と使用方法を確認してください。rng-tools パッケージには、ハードウェアで生成されたエントロピーを に供給するデーモンが含まれています/dev/random
。
システムで HRNG が利用できず、パフォーマンスのためにエントロピー強度を犠牲にしても構わないと思っている場合は、 から/dev/random
のデータを使用して適切な PRNG をシードし、PRNG に大部分の作業を任せることができます。SP800-90には、簡単に実装できるNIST 承認の PRNG がいくつかリストされ ています。
あなたが参照した問題/dev/random
は、SecureRandom
アルゴリズムではなく、使用するランダム性のソースにあります。2つは直交しています。2 つのうちどちらが速度を落としているかを把握する必要があります。
リンクした Uncommon Maths ページでは、ランダム性の原因に対処していないことが明示的に言及されています。
BouncyCastle などのさまざまな JCE プロバイダーを試して、それらの実装SecureRandom
がより高速かどうかを確認できます。
簡単に検索すると、デフォルトの実装を Fortuna に置き換える Linux パッチも見つかります。これ以上のことはわかりませんが、よろしければ調べてみてください。
また、不適切に実装されたアルゴリズムや乱数ソースを使用することは非常に危険SecureRandom
ですが、独自の JCE プロバイダーをカスタム実装することもできますSecureRandomSpi
。プロバイダに署名してもらうには、Sun とのプロセスを経る必要がありますが、実際には非常に簡単です。暗号化ライブラリーに関する米国の輸出制限を認識していることを示すフォームを FAX で送信するだけです。
Java 8 を使用して、Linux で呼び出しSecureRandom.getInstanceStrong()
を行うとNativePRNGBlocking
アルゴリズムが得られることがわかりました。多くの場合、これは数バイトのソルトを生成するために何秒間もブロックされます。
代わりに明示的に要求するように切り替えましたがNativePRNGNonBlocking
、名前から予想されるように、ブロックされなくなりました。これがセキュリティにどのような影響を与えるのかわかりません。おそらく、ノンブロッキング バージョンは、使用されるエントロピーの量を保証できません。
更新:わかりました、この優れた説明を見つけました。
簡単に言えば、ブロックを回避するには、 を使用しますnew SecureRandom()
。これは/dev/urandom
、ブロックせず、基本的に と同じくらい安全な を使用し/dev/random
ます。投稿から:「/dev/random を呼び出したいのは、マシンが最初に起動し、エントロピーがまだ蓄積されていないときだけです」。
SecureRandom.getInstanceStrong()
絶対に最強の RNG を提供しますが、ブロックの束が影響しない状況でのみ安全に使用できます。
システムに人工的なランダム性を与えるツールがあります (少なくとも Ubuntu では)。コマンドは次のとおりです。
rngd -r /dev/urandom
また、前に sudo が必要になる場合があります。rng-tools パッケージがない場合は、インストールする必要があります。私はこれを試しました、そしてそれは間違いなく私を助けました!
ソース:マット対世界
私は同じ問題に直面しました。適切な検索用語でグーグル検索した後、 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
ここに投稿しました
私はこの問題に直面したことはありませんが、プログラムの開始時にスレッドを生成し、すぐにシードを生成しようとし、その後終了します。ランダムに呼び出すメソッドは、それが生きている場合はそのスレッドに参加するため、最初の呼び出しは、プログラム実行の非常に早い段階で発生した場合にのみブロックされます。
RNG の要件をより明確にする必要があるようです。最も強力な暗号化 RNG 要件 (私が理解しているように) は、それらを生成するために使用されるアルゴリズムを知っていても、以前に生成された乱数をすべて知っていても、非現実的な量のコンピューティング パワーを費やすことなく、未来を実現します。
このランダム性の完全な保証が必要ない場合は、おそらく適切なパフォーマンスのトレードオフがあります。Uncommons-Maths または Fortuna の AESCounterRNG に関するDan Dyer の回答に同意する傾向があります(その著者の 1 人は、暗号化の専門家である Bruce Schneier です)。どちらも使用したことはありませんが、アイデアは一見評判が良いようです。
初期のランダム シードを定期的に (たとえば、1 日または 1 時間に 1 回など) 生成できれば、高速ストリーム暗号を使用して、ストリームの連続するチャンクから乱数を生成できると思います (ストリーム暗号が XOR を使用する場合は、 null のストリームを渡すか、XOR ビットを直接取得します)。ECRYPTのeStreamプロジェクトには、パフォーマンス ベンチマークを含む多くの優れた情報があります。これでは、補充する時点の間でエントロピーが維持されないため、誰かが乱数の 1 つと使用したアルゴリズムを知っていれば、技術的には、多くの計算能力を使用して、ストリーム暗号を解読し、内部状態を推測して、将来の乱数を予測できるようにします。ただし、そのリスクとその結果が、エントロピーを維持するコストを正当化するのに十分かどうかを判断する必要があります。
編集:これは、このトピックに非常に関連しているように見えるネットで見つけたRNGに関するいくつかの暗号化コースノートです。
私の経験では、その後のランダム データの生成ではなく、PRNG の初期化が遅いだけでした。より積極的な初期化戦略を試してください。作成にはコストがかかるため、シングルトンのように扱い、同じインスタンスを再利用してください。1 つのインスタンスでスレッドの競合が多すぎる場合は、それらをプールするか、スレッド ローカルにします。
乱数の生成に妥協しないでください。そこに弱点があると、すべてのセキュリティが危険にさらされます。
COTS アトミック崩壊ベースのジェネレーターはあまり見当たりませんが、大量のランダム データが本当に必要な場合は、いくつかの計画があります。HotBits を含め、常に注目すべき興味深いサイトの 1 つに、John Walker の Fourmilab があります。
ハードウェアがサポートしている場合は、私が作成したJava RdRand ユーティリティを使用してみてください。
これは Intel の命令に基づいており、大量実装のRDRAND
場合よりも約 10 倍速く、帯域幅の問題はありません。SecureRandom
この実装は、命令を提供する CPU でのみ機能することに注意してください (つまり、rdrand
プロセッサ フラグが設定されている場合)。RdRandRandom()
コンストラクターを介して明示的にインスタンス化する必要があります。特にProvider
実装されていません。
再帰アルゴリズムの初期化ソースとしてセキュア ランダムを使用します。UncommonMath の代わりに Mersenne twister を大量の作業に使用できます。UncommonMath はしばらく前から存在し、他の prng よりも優れていることが証明されています。
http://en.wikipedia.org/wiki/Mersenne_twister
時々、初期化に使用される安全な乱数を更新してください。たとえば、クライアントごとに 1 つのメルセンヌ ツイスター疑似乱数発生器を使用して、クライアントごとに 1 つの安全な乱数を生成し、十分に高い程度のランダム化を取得することができます。
他に確認する必要があるのは、ファイル lib/security/java.security のプロパティ securerandom.source です。
/dev/random ではなく /dev/urandom を使用すると、パフォーマンスが向上する場合があります。乱数の品質が重要な場合は、セキュリティを損なう妥協をしないでください。