1. 程式人生 > >javax.net.ssl.SSLHandshakeException的解決辦法

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的建立過程中,證書是非常關鍵的配置,強烈建議採用證書的方案解決連線問題,如果只是臨時的解決方案與開發測試聯通性,本文給出了另外一種解決方案。

參考文章