異常資訊如下:
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
原因:伺服器的認證不被信任。一般是這樣造成的。
使用KEYTOOL工具建立認證,然後用TOMCAT啟動後,在瀏覽器開啟網站時,會出現認證不被信任的提示。當然,利用HTTPCLIENT向服務端HTTPS發送資料時,HTTPCLIENT也會檢測服務端的認證是否被信任,不被信任就拋出上面的異常。
解決辦法有兩種,一種是使認證被用戶端信任。另一種是使用HTTPCLIENT發送資料時不檢測伺服器憑證是否可信。
第一種辦法,使認證被信任。
找正規CA簽發認證,或者自己簽發認證(只能那一台客戶機上可信)。找正規CA簽發認證就不說了,自己簽發認證呢,見我的其他文章。
我發現,自己簽名的認證弄好之後,從用戶端開啟服務端地址時,不再提示上面的錯誤,但是還是不能發送資料。原因是什麼呢?因為那台認證在用戶端作業系統上可信,但是在JAVA的KEYSTORE裡不可信,要把服務端的認證匯入KEYSTORE庫中
匯入辦法:
開啟命令列視窗,併到<java-home>\lib\security\ 目錄下,運行下面的命令:
keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
最後一個是服務端匯出的認證,其他可以預設。
要注意的是,如果用戶端電腦上裝有許多個JAVA版本,要確定你匯入的認證的JAVA版本是你TOMCAT使用的那個,一般TOMCAT使用的是環境變數指向的那個JAVA版本。
如果是在ECLIPSE中建立的TOMCAT伺服器,建立時會要你選擇預設JRE還是指向的JAVA,這裡一定要選指向剛才匯入的那個JAVA的路徑,不然,你匯入的認證庫也沒效果。
第二種辦法,使用HTTPCLIENT時不檢測伺服器憑證是否可信
擴充HttpClient 類實現自動接受認證
因為這種方法自動接收所有認證,因此存在一定的安全問題,所以在使用這種方法前請仔細考慮您的系統的安全需求。具體的步驟如下:
•提供一個自訂的socket factory (test.MySecureProtocolSocketFactory )。這個自訂的類必須實現介面org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory ,在實現介面的類中調用自訂的X509TrustManager(test.MyX509TrustManager) ,這兩個類可以在隨本文帶的附件中得到
•建立一個org.apache.commons.httpclient.protocol.Protocol 的執行個體,指定協議名稱和預設的連接埠號碼
Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
•註冊剛才建立的https 協議對象
Protocol.registerProtocol("https ", myhttps);
•然後按照普通編程 方式開啟https 的目標地址,代碼如下:
MySecureProtocolSocketFactory.java
import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory { private SSLContext sslcontext = null; private SSLContext createSSLContext() { SSLContext sslcontext=null; try { sslcontext = SSLContext.getInstance("SSL"); sslcontext.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } return sslcontext; } private SSLContext getSSLContext() { if (this.sslcontext == null) { this.sslcontext = createSSLContext(); } return this.sslcontext; } public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket( socket, host, port, autoClose ); } public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket( host, port ); } public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort); } public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); } int timeout = params.getConnectionTimeout(); SocketFactory socketfactory = getSSLContext().getSocketFactory(); if (timeout == 0) { return socketfactory.createSocket(host, port, localAddress, localPort); } else { Socket socket = socketfactory.createSocket(); SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); SocketAddress remoteaddr = new InetSocketAddress(host, port); socket.bind(localaddr); socket.connect(remoteaddr, timeout); return socket; } } //自訂私人類 private static class TrustAnyTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }