使用 SecureRandom 產生隨機數-坑


背景:

項目里用到 Random 的地方定義一下 Random 變量(如下)

 

 

在用 sonar 進行檢查時,Sonar 建議使用 SecureRandom.getInstanceStrong() 來初始化,如下:

private Random rand = SecureRandom.getInstanceStrong();

於是將其改成 sonar 建議的形式來生成隨機數

問題:

發版全量結果問題來了,第三方接口調用超時,從我們的監控來看,接口執行阻塞超時,看起來像陷入了某種死循環。

 

 

 

 

 

 

 

問題排查:

 

復現問題:首先使用了相同的請求參數在預發進行了測試,但令人不解的是,問題無法復現。隨后又測試了線上機器,可以穩定的復現問題。這時一臉黑人問號。

 

打日志:為了定位問題,在代碼的關鍵位置插入日志,經過多次的發布定位到問題,執行到 SecureRandom.getInstanceStrong() 方法后就阻塞了。

問題原因:

 

SecureRandom.getInstanceStrong() 方法在 linux 環境下使用 /dev/random 生成種子。但是 /dev/random 是一個阻塞數字生成器,如果它沒有足夠的隨機數據提供,它就一直等,這迫使 JVM 等待。鍵盤和鼠標輸入以及磁盤活動可以產生所需的隨機性或熵。但在一個缺乏這樣的活動服務器,可能會出現問題,當系統的熵池中數量不足時,就會阻塞當前線程。

Linux 內核采用熵來描述數據的隨機性,熵(entropy)是描述系統混亂無序程度的物理量,一個系統的熵越大則說明該系統的有序性越差,即不確定性越大。內核維護了一個熵池用來收集來自設備驅動程序和其它來源的環境噪音。理論上,熵池中的數據是完全隨機的,可以實現產生真隨機數序列。為跟蹤熵池中數據的隨機性,內核在將數據加入池的時候將估算數據的隨機性,這個過程稱作熵估算。熵估算值描述池中包含的隨機數位數,其值越大表示池中數據的隨機性越好。 內核中隨機數發生器 PRNG 為一個字符設備 random,代碼實現在 drivers/char/random.c,該設備實現了一系列接口函數用於獲取系統環境的噪聲數據,並加入熵池。系統環境的噪聲數據包括設備兩次中斷間的間隔,輸入設備的操作時間間隔,連續磁盤操作的時間間隔等。 對應的接口包括  (學習來源:https://my.oschina.net/lieefu/blog/549455

 

該接口會返回指定字節數的隨機數。random 設備了提供了 2 個字符設備供用戶態進程使用——/dev/random 和/dev/urandom:

 

  • /dev/random 適用於對隨機數質量要求比較高的請求,在熵池中數據不足時, 讀取 dev/random 設備時會返回小於熵池噪聲總數的隨機字節。/dev/random 可生成高隨機性的公鑰或一次性密碼本。若熵池空了,對/dev/random 的讀操作將會被阻塞,直到收集到了足夠的環境噪聲為止。這樣的設計使得/dev/random 是真正的隨機數發生器,提供了最大可能的隨機數據熵。
  • /dev/urandom,非阻塞的隨機數發生器,它會重復使用熵池中的數據以產生偽隨機數據。這表示對/dev/urandom 的讀取操作不會產生阻塞,但其輸出的熵可能小於/dev/random 的。它可以作為生成較低強度密碼的偽隨機數生成器,對大多數應用來說,隨機性是可以接受的。

 

 

 

解決方法:

有了以上的的解釋,我們就知道解決方案了,使用 /dev/urandom 這種非阻塞的方式來產生隨機數即可,在 Java 中我們這樣寫即可

SecureRandom random = new SecureRandom();

new SecureRandom() 使用 /dev/urandom 生成種子,不會產生阻塞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM