jdk6出現peer not authenticated問題緣由分析與解決

因爲公司系統與其餘系統進行通信,時不時報出「peer not authenticated」這個錯誤,因而對這個錯誤進行分析。java

1、場景重現

  1. 一臺tomcat,自制證書,配置https方式訪問,怎麼用jdk生成密鑰,怎麼配置,網上不少文章,tomcat的server.xml配置以下(不須要放項目,空項目啓動便可)
  2. <Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
                   maxThreads="150" scheme="https" secure="true"
                   clientAuth="false" sslProtocol="TLS"
    			   keystoreFile="e:/tomcat.keystore" keystorePass="tomcat"/>
  3. 瀏覽器能夠直接訪問
  4. java客戶端用httpclient訪問(httpURLConnection同樣的,底層都是socket)
  5. 客戶端用jdk6訪問,報錯:peer not authenticated
  6. 客戶端將jdk改成jdk8,能夠成功請求。

2、先說解決方案

針對服務端用tomcat,客戶端用jdk6形成這個報錯的狀況,先說下解決方案,主要有如下幾個,1和2爲客戶端修改,3和4爲服務端修改nginx

  1. 客戶端將jdk修改成1.8
  2. 客戶端修改jdk源碼,將sun.security.ssl.ProtocolVersion(jdk1.6)的DEFAULT_HELLO設置爲「TLSv1」,並替換原有jdk中該類(1.8在sun.security.ssl包中),jdk8默認就是使用TSLv1 
  3. 服務端改用nginx作代理,在nginx上配置https(親測可行)
  4. 服務端tomcat的service.xml的connector中配置sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2,SSLv2Hello",即增長SSLv2Hello的支持  

 

(1.6的ProtocolVersion類,設置FIPS爲true也可)數組

3、問題分析流程

    1. 網上找資料,發現不少說要本身配置一個X509TrustManager跳過證書驗證,實際上這一點已經作了,然而沒有解決問題。瀏覽器

    2. 分析https的socket工做原理流程圖以下tomcat

       

(https通信流程)安全

      

    3. 通過堆棧查看,發現證書不存在,也就是說重寫X509TrustManager是繞過證書驗證,但問題是連服務器的證書都未獲取到,如何繞過驗證呢?因此網上這些文章未解決這個問題。(圖1爲jdk6時的堆棧信息,certs爲null,圖2爲jdk8時候的堆棧信息,獲取到了certs)服務器

    

(jkd6 httpclient未獲取到服務器證書)socket

(jdk8 httpclient獲取到了服務器證書)maven

    4. ok,那既然jdk6不行,jdk8能夠,那麼這二者發的消息有什麼不同呢?客戶端請求代碼中加上這一段:學習

System.setProperty("javax.net.debug","ssl");

將socket通信記錄打印出來,發如今client發送Hello請求的時候,

jdk6是這樣的:

main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, handshake_failure

 

(jdk6打印socket日誌)

jdk8是這樣的:

main, WRITE: TLSv1.2 Handshake, length = 235
main, READ: TLSv1 Handshake, length = 686;

 

 (jdk8打印socket日誌)

jdk6用的SSLv2,jdk8用的TLSv1

    5. 那麼服務端的tomcat是如何處理的呢?(服務端用的tomcat6+jdk6,經測試,tomcat8+jdk8是同樣的狀況)

jdk1.6發送過來的SSLV2Hello請求,被handleUnknowRecord這個方法處理,其中有個判斷

if(this.helloVersion != ProtocolVersion.SSL20Hello){

    throw new SSLHandshakeException("SSLv2Hello is disabled").

 }

從這裏拋出了異常,因此沒有返回證書,握手失敗。可是jdk1.8則走的readV3Record方法,正常執行,就不深刻看代碼了。

 

 

(jdk6請求時,服務端走handleUnknowRecord,能夠看到,this.helloVersion是TLSv1,而不是SSLv2Hello,拋出異常,服務端不返回證書)

 

    6.  那麼就有疑問了,客戶端發出的是SSLV2的握手請求,可是服務端說「你的握手請求,不是SSLV2的請求,因此不容許經過」,!!!!!???!?!?!??!?!?這不是本身打臉麼。。

好吧,那麼繼續看服務端源代碼解答疑問。

    7. tomcat接收socket處理流程

        1)tomcat接收socket,tomcat是採用2個線程在處理的,一個是接收線程,一個是任務處理線程,接收線程接收請求後進行初始化,而後交給任務線程去處理(二者處理的socket對象id相同),因此咱們看到當有socket請求的時候,tomcat這邊的堆棧是這樣一個狀況:

    2)接收線程接收到後,要進行初始化,在com.sun.net.ssl.internal.ssl.InputRecord的setHelloVersion方法打個斷點,跟進堆棧。接收者經過阻塞隊列,接收到socket請求

    3)進入serverSocketFactory.acceptSocket(serverSocket)方法,acceptSocket接收到socket,並執行doneConnect這個方法進行一些參數設置。

    4)那麼咱們看下localSSLSocketImpl.doneConnect();這段代碼中,localSSLSocketImpl的握手協議是什麼?會發現,剛進入時候,握手協議是SSLv2Hello

 

    5)一層層跟進,發現有一處對這個對象的helloVersion作了改動,代碼以下:

    6)那麼這個localProtocolVersion是哪裏來的呢?一層層往外找,發現最外層傳進來的,爲this.enabledProtocols,而且看到this.enabledProtocols裏面的值即爲TLSv1

 

   8.修改下tomcat server.xml配置,增長sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2,SSLv2Hello",明確使其支持SSLv2Hello協議,再調試一遍,發現this.enabledProtocols的helloVersion變爲了SSLv2Hello

<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" 
               sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2,SSLv2Hello"
               keystoreFile="e:/tomcat.keystore" keystorePass="tomcat"/>

結論

經過分析發現

一、jdk6若是FIPS是false(幹啥用的我也不清楚,其餘時間再學習),默認用SSLv2Hello協議發送hello請求,而jdk8則默認用TLSv1

二、若是服務器是tomcat,tomcat若是未明確配置sslEnabledProtocols支持"SSLv2Hello",則默認爲TLSV1,不支持SSLv2Hello

三、tomcat接收到客戶端socket的hello請求後,強制將協議改成TLSV1

四、根據socket的握手報文數組中第一個元素的內容是否爲20或22,jdk6發出來的請求將會調用handleUnknowRecord方法(jdk8發出來的走readV3Record方法,具體爲什麼不必繼續研究)

五、handleUnknowRecord方法中會判斷,若是協議非SSLv2Hello,則不容許經過,不會給客戶端返回證書,服務端報錯「SSLV2Hello is disabled」,客戶端報錯「peer not authenticated」

其餘說明

一、tomcat源碼運行,只需本身新建maven項目,並將下載的源碼中conf和java包分別放入新項目的代碼和conf中便可,而且須要配置conf的輸出目錄,tomcat有讀文件的控制

二、推薦不要用tomcat配置證書,用nginx配置證書比較好

三、TLS能夠理解爲SSL的升級版本,更安全,SSL有漏洞,因此tomcat開啓也不要開啓對SSL的支持,而是隻開啓SSLv2Hello的支持

相關文章
相關標籤/搜索