zoukankan      html  css  js  c++  java
  • peer not authenticated的终极解决方案

    一、前述

      使用httpclient发起https请求时,可能会遇到如下异常:

    javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
        at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
        at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
        at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:399)
        at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:143)

      网上搜索也能找到一大堆的解决方案,但大部分都类似,就是跳过证书的验证,于是跟着稀里糊涂的将代码拷贝下来使用,结果呢?有的能解决,有的依旧报这个错误。到底咋回事呢,接下来就说说这个问题的解决方案。

    二、缘由

      首先,要知道导致报这个异常的原因不仅仅是因为证书校验不通过。

      都知道,在我们通过https链接服务器时,服务器会给我们返回一个证书,这个证书可能经过CA认证,也可能是未认证的自制证书,客户端拿到这个证书后会对这个证书进行验证,如果是经过CA验证的证书,自然证书校验就能通过,自制证书自然就校验不同过,从而导致上边的异常。

      证书校验通过后,还需要校验访问的域名是否和证书指定的域名是否匹配。未匹配也会导致如上异常。

      上边两步都校验通过了才开始进行握手,但握手也有可能失败,从而导致上边的异常。

      以上三个步骤中任何一个出了问题,都会连接失败。

    三、解决方法

      通过网上搜索我们大部分都会找到类似如下的解决方案:

    SSLContext sslContext;
        try {
            sslContext = SSLContext.getInstance("SSL");
            // set up a TrustManager that trusts everything
            try {
                sslContext.init(null,
                        new TrustManager[] { new X509TrustManager() {
                            public X509Certificate[] getAcceptedIssuers() {
                                return null;
                            }
    
                            public void checkClientTrusted(
                                    X509Certificate[] certs, String authType) {
                            }
    
                            public void checkServerTrusted(
                                    X509Certificate[] certs, String authType) {
                            }
                        } }, new SecureRandom());
            } catch (KeyManagementException e) {
            }
             SSLSocketFactory ssf = new SSLSocketFactory(sslContext,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

      这个解决方案针对以上的三个步骤中的第一步,就是放弃对证书的校验,但是第二部还可能有问题,要想彻底解决还需要跳过对域名的校验。这里给出最终的解决方案:

    try {
                    SSLContext sslContext = SSLContext.getInstance("TLS");
                    X509TrustManager tm = new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    
                        }
    
                        @Override
                        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    
                        }
    
                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                    };
                    sslContext.init(null,new TrustManager[]{tm},null);
                    sslSocketFactory = new SSLSocketFactory(sslContext,new X509HostnameVerifier(){
                        @Override
                        public boolean verify(String s, SSLSession sslSession) {
                            return true;
                        }
    
                        @Override
                        public void verify(String host, SSLSocket ssl) throws IOException {
    
                        }
    
                        @Override
                        public void verify(String host, X509Certificate cert) throws SSLException {
    
                        }
    
                        @Override
                        public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
    
                        }
                    });
                } catch (GeneralSecurityException e) {
                    log.error("create SSLSocketFactory error:{}",e);
                }
                return sslSocketFactory;

      这里不仅放弃对证书的校验,也放弃对hostname的校验,通过空实现X509HostnameVerifier类。

      以上这个解决方案就会彻底解决问题吗?不一定,还有一个步骤就是握手的步骤也可能出问题,怎么判断是不是握手步骤出了问题呢?可以在代码里做如下设置:

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

      做了以上设置后,就可以打印https建立连接的日志了,如下:

    true
    addingastrustedcert:
    证书内容略去
    triggerseedingofSecureRandom
    doneseedingSecureRandom
    executingrequestGETHTTP/1.1
    Ignoringunavailableciphersuite:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    Ignoringunavailableciphersuite:TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    Ignoringunavailableciphersuite:TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
    Ignoringunsupportedciphersuite:TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
    Ignoringunsupportedciphersuite:TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
    Ignoringunsupportedciphersuite:TLS_RSA_WITH_AES_256_CBC_SHA256
    Ignoringunavailableciphersuite:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
    Ignoringunsupportedciphersuite:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
    Ignoringunavailableciphersuite:TLS_DHE_DSS_WITH_AES_256_CBC_SHA
    Ignoringunsupportedciphersuite:TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
    Ignoringunsupportedciphersuite:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
    Ignoringunsupportedciphersuite:TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
    Ignoringunavailableciphersuite:TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
    Ignoringunavailableciphersuite:TLS_RSA_WITH_AES_256_CBC_SHA
    Ignoringunsupportedciphersuite:TLS_RSA_WITH_AES_128_CBC_SHA256
    Allowunsaferenegotiation:false
    Allowlegacyhellomessages:true
    Isinitialhandshake:true
    Issecurerenegotiation:false
    %%Nocachedclientsession
    ***ClientHello,TLSv1
    ……
    main,WRITE:TLSv1Handshake,length=181
    main,READ:TLSv1Alert,length=2
    main,RECVTLSv1ALERT:fatal,handshake_failure
    main,calledcloseSocket()
    main,handlingexception:javax.net.ssl.SSLHandshakeException:Receivedfatalalert:handshake_failure

      通过最后一个词组“handshake_failure”,你一定可以确定是握手失败了。这一般是因为客户端的加密机制太简单,服务器认为不安全,握手失败。

      握手失败解决方案就比较简单,下载一个UnlimitedJCEPolicyJDK7.zip 。在http://www.oracle.com/technetwork/java/javase/downloads/index.html下载就好了。里面包含了两个jar。在你的/lib/security,替换后,重新运行看看。

      如果以上这些还没能解决,那我也是不知道了,可以留言,我跟你一块找答案^_^。

  • 相关阅读:
    PE格式第五讲,手工添加节表
    PE格式第四讲,数据目录表之导入表,以及IAT表
    PE格式第三讲扩展,VA,RVA,FA(RAW),模块地址的概念
    PE文件格式详解,第三讲,可选头文件格式,以及节表
    PE文件格式详解,第二讲,NT头文件格式,以及文件头格式
    LVS
    Haproxy
    Nginx
    MySQL入门第一天——概述、数据表与约束操作
    NoSQL入门第五天——Java连接与整合操作
  • 原文地址:https://www.cnblogs.com/metoy/p/6238061.html
Copyright © 2011-2022 走看看