1. 程式人生 > >偶遇 JDK 1.8 還未修復的 SecureRandom.getInstance("SHA1PRNG") 之 bug

偶遇 JDK 1.8 還未修復的 SecureRandom.getInstance("SHA1PRNG") 之 bug

樓主今天興高采烈的在部署環境,下載 JDK,打包專案,上傳至伺服器。

配置 JDK ,打包上傳專案樓主就不在這裡重複了,讀者自行解決哈!

 

1. 啟動專案

java -jar xxxx.jar 

令樓主沒有想到的是:程式卡主了,卡在了資料庫建立連線的位置。(檢視方法方式: jstack <pid> 即可)

 

2. 堆疊資訊

由於是專案剛一啟動,初始化資料庫連線池,並沒有太多的執行緒堆疊。這裡我貼一下我遇到的主要問題的堆疊資訊:


"restartedMain" #11 prio=5 os_prio=0 tid=0x00007f4430002800 nid=0x65b0 runnable [0x00007f447837a000]
   java.lang.Thread.State: RUNNABLE
	at java.io.FileInputStream.readBytes(Native Method)
	at java.io.FileInputStream.read(FileInputStream.java:255)
	at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)
	at sun.security.provider.SeedGenerator.generateSeed(SeedGenerator.java:144)
	at sun.security.provider.SecureRandom$SeederHolder.<clinit>(SecureRandom.java:203)
	at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
	- locked <0x00000000d6eb8930> (a sun.security.provider.SecureRandom)
	at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
	at oracle.security.o5logon.O5Logon.a(Unknown Source)
	at oracle.security.o5logon.O5Logon.<clinit>(Unknown Source)
	at oracle.jdbc.driver.T4CTTIoauthenticate.<init>(T4CTTIoauthenticate.java:566)
	at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:370)
	at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:546)
	at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:236)
	at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
	at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:521)

或許你會問我,為什麼你要定位到這個執行緒?主要是因為道友經過好幾次 jstack 操作,發現該執行緒一直都停留在該位置,這就足以說明這個執行緒是當前執行緒!

 

3. 分析堆疊資訊

從上面的堆疊資訊中,我們可以得知那些資訊?

  • 當前執行緒名稱為 : restartedMain
  • 當前執行緒狀態為: RUNNABLE
  • 當前執行緒 ID: 0x00007f4430002800
  • native 執行緒 ID: 0x65b0
  • 當前執行位置 : at java.io.FileInputStream.readBytes(Native Method)

通過分析堆疊,想必讀取檔案不會有什麼問題,我們往執行緒堆疊的上層追蹤。目標點落在 

    at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)

很明顯,這裡是  JDK 原始碼的呼叫,接下來打卡 jdk 原始碼進行檢視!

 

 

4. 原始碼分析

我們通過堆疊資訊一步步跟進原始碼檢視,

從方法  nextBytes 上來看,他呼叫這個是為了:生成使用者指定的隨機位元組數。好吧,那就是它在生成的時候,需要讀取某個檔案來生成隨機數。

 

5. 寫個測試驗證想法

[ryan@ryanmacbook ~]$ vim HelloWorld.java 
import java.security.SecureRandom;

class HelloWorld {

    public static void main(String args[]) throws Exception {

        byte[] bytes = new byte[32];

        SecureRandom.getInstance("SHA1PRNG").nextBytes(bytes);

        System.out.println("SourceRandom nextBytes : " + new String(bytes));
    }
}

緊接著執行

javac HelloWorld.java

這個要是讀者你不會,那你就放棄java吧,java 界可能不適合你。

接下來執行 

java HelloWorld

執行後,,的結果是 

[ryan@ryanmacbook ~]$ java HelloWorld


^C
[ryan@ryanmacbook ~]$

卡住了,樓主強制 kill 了。

 

說明我們的問題點就是這裡。

 

接下來,解決辦法是啥?你要問樓主樓主也不知道,咋辦?上百度唄,百度都不會用的程式設計師不是好程式設計師:

 

6. 解決辦法

最後的解決辦法就是 ,在專案啟動的時候,修改 java.security.egd,通過如下方式,專業術語叫做    熵池策略   

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

問題的本質還是 Oracle 驅動類中呼叫了 SecureRandom.nextBytes() 。樓主還沒來得及看其他驅動程式是否也有這個問題,當然也不排除與作業系統有關係。待驗證其他作業系統後,再補充!

 

果斷成功!

 

道友查了下,說是 JVM 的 bug , 在收集噪點的時候沒有收集全導致的。如果你的也有遇到這樣的問題,時而可用,時而不可用,那就是噪點收集的問題。

 

[參考連結] http://ifeve.com/jvm-random-and-entropy-source/