httpsurlconnection 无报错提示建立连接

java使用HttpsUrlConnection下载一个远程文件时,常规的方法会出现不信任证书的异常,解决的方法就是:使用自信认管理器,示例如下:

Java中忽略對建立SSL連線之certificate的檢查

 

建立SSL連線時,伺服器會提供一份電子憑證(certificate),以供用戶端程式在連線時用以編解與伺服器之間的訊息交換。正常的情況下,在建立連線之際,用戶端程式收到伺服器提供的ceritificate,會向這張ceritifccate的憑證授權單位(CA)要求檢驗此張憑證的有效性。

有許多網站,尤其是還在測試中的網站,因為向憑證授權單位購買憑證是需要花錢的,所以都會自行利用一些簽證的工具,自行產生憑證,一般來說,就被稱為是 self-signed的certificate。當我們透過像IE這樣子的瀏覽器來檢視網址為https://開頭的網站時,倘若看到下述的視窗:

 

 

通常都是遇上self signed的網站。瀏覽器處理這種情況很簡單,因為它可以詢問使用者是否願意接受,再進行下一步的處理。但是使用Java來連線此類的網站時,事情就比較複雜。倘若我們寫下下述的程式碼:

import java.io.*;

import java.net.*;

 

public class TestCert

{

      public static void main(String argv[])

      {

             try

             {

                   String _url = "https://www.cs.nthu.edu.tw/";

                   URL url = new URL(_url);

                   URLConnection uc = url.openConnection();

                   InputStream is = uc.getInputStream();

                   InputStreamReader isr = new InputStreamReader(is);

                   BufferedReader br = new BufferedReader(isr);

                   String s = br.readLine();

                   System.out.println(s);

                   br.close();

                   isr.close();

             }

             catch(Exception e)

             {

                   e.printStackTrace();

             }

      }

}

這段程式碼基本上就是連接到一個https://的網站,但由於這個網站的憑證是self signed的,所以發生了下述的錯誤:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderExce

ption: unable to find valid certification path to requested target

        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)

        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1

518)

這便是Java的SSL socket實作在收到伺服器回傳的憑證時,由於無法認證該憑證,而引發的錯誤。有一個解決的方法,便是把這個憑證加到你Java執行環境中儲存信任憑證的地方,不過這不是本篇blog的主旨,本篇的主旨在於如何忽略檢查,方法如下:

 

import javax.net.ssl.*;

 

TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()

{

        public java.security.cert.X509Certificate[] getAcceptedIssuers()

        {

                return null;

        }

        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)

        {

        }

 

        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)

        {

        }

}};

SSLContext sc = SSLContext.getInstance("SSL");

sc.init(null, trustAllCerts, new java.security.SecureRandom());

HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

基本上,就是為目前SSL的SocketFactory設定一個自訂的TrustManager,並且在TrustManager該有的介面中,pass所有的檢查。如此一來,便可以忽略掉上述所提到的錯誤。

使用上述的方式,必須小心整個application是否會連線到不在預期中的網站,因為這樣子的寫法,等於是開放完全不檢查來自所有網站的憑證是否有效。我是偷懶的人,所以不想把憑證加到信任的cert store去,但因為明確的知道憑證的來源網站,所以才使用忽略的方式,倘若你的情況並非如此,就應該審慎的評估。

另外,如果你所收到的憑證,其中的domain name並未和其實際的domain name相符(我有遇到domain name填IP address的情況:p),也會引發憑證的檢查錯誤,解決之道(其實就是忽略之道)如下:

HostnameVerifier hv = new HostnameVerifier()

{

      public boolean verify(String urlHostName, SSLSession session)

      {

             System.out.println("Warning: URL Host: "+urlHostName+" vs. "+session.getPeerHost());

             return true;

      }

};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

為java.net.ssl.HttpsURLConnection設定一個自訂的HostnameVerifier,並且在其verify()中回傳檢查成功(true),就可以把對憑證host name的檢查予以忽略。

以上兩種情況,約略是連接至測試用之https網站的主要問題。

posted on 2010-12-07 16:44  aurawing  阅读(1197)  评论(0编辑  收藏  举报