javax.net.ssl.SSLHandshakeException的解决办法

在SOAP协议的连接过程中,出现如下错误:

Caused by: BaseConnection$BasicConnectionException: failed to connect: HTTP 传输错误: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: 
    ... 33 more
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    ... 34 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)
    ... 62 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    ... 68 more

显然,这是证书导致的连接失败,首先测试了一下网上精简版的解决方案:

static {
    HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equals("192.168.101.33"));
}

结果依旧失败,依照网上的说法,在使用IP地址进行SSH连接时,证书中必须要有主题的替代名称,如下:

Unlike some browsers, Java follows the HTTPS specification strictly when it comes to the server identity verification (RFC 2818, Section 3.1) and IP addresses.

When using a host name, it's possible to fall back to the Common Name in the Subject DN of the server certificate, instead of using the Subject Alternative Name.

When using an IP address, there must be a Subject Alternative Name entry (of type IP address, not DNS name) in the certificate.

听起来很复杂,但是证书真能解决此问题,在此不进行测试说明,而是采用放弃证书的一种解决办法,如下:

//  直接通过主机认证
HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName, SSLSession session) {
        return true;
    }
};
//  配置认证管理器
javax.net.ssl.TrustManager[] trustAllCerts = {new TrustAllTrustManager()};
SSLContext sc = SSLContext.getInstance("SSL");
SSLSessionContext sslsc = sc.getServerSessionContext();
sslsc.setSessionTimeout(0);
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
//  激活主机认证
HttpsURLConnection.setDefaultHostnameVerifier(hv);

TrustAllTrustManager的实现如下:

public class TrustAllTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {

    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
        return true;
    }

    public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
        return true;
    }

    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            throws java.security.cert.CertificateException {
        return;
    }

    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            throws java.security.cert.CertificateException {
        return;
    }

}

结论

在SSL的创建过程中,证书是非常关键的配置,强烈建议采用证书的方案解决连接问题,如果只是临时的解决方案与开发测试联通性,本文给出了另外一种解决方案。

参考文章

证书服务器的名称是怎样解析的?

你可能感兴趣的:(java)