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的支持

相关文章
相关标签/搜索