java訪問https時,忽略證書信任問題


java程序在訪問https資源時,出現報錯

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

這本質上,是java在訪問https資源時的證書信任問題。如何解決這個問題呢?

 

為何有這個問題?

解決這個問題前,要了解

1)https通信過程

 

客戶端在使用HTTPS方式與Web服務器通信時有以下幾個步驟,如圖所示。

(1)客戶使用https的URL訪問Web服務器,要求與Web服務器建立SSL連接。

(2)Web服務器收到客戶端請求后,會將網站的證書信息(證書中包含公鑰)傳送一份給客戶端。

(3)客戶端的瀏覽器與Web服務器開始協商SSL連接的安全等級,也就是信息加密的等級。

(4)客戶端的瀏覽器根據雙方同意的安全等級,建立會話密鑰,然后利用網站的公鑰將會話密鑰加密,並傳送給網站。

(5)Web服務器利用自己的私鑰解密出會話密鑰。

(6)Web服務器利用會話密鑰加密與客戶端之間的通信。

 

2)java程序的證書信任規則

如上文所述,客戶端會從服務端拿到證書信息。調用端(客戶端)會有一個證書信任列表,拿到證書信息后,會判斷該證書是否可信任。

如果是用瀏覽器訪問https資源,發現證書不可信任,一般會彈框告訴用戶,對方的證書不可信任,是否繼續之類。

Java虛擬機並不直接使用操作系統的keyring,而是有自己的security manager。與操作系統類似,jdk的security manager默認有一堆的根證書信任。如果你的https站點證書是花錢申請的,被這些根證書所信任,那使用java來訪問此https站點會非常方便。因此,如果用java訪問https資源,發現證書不可信任,則會報文章開頭說到的錯誤。

 

 

解決問題的方法

1)將證書導入到jdk的信任證書中(理論上應該可行,未驗證)

2)在客戶端(調用端)添加邏輯,忽略證書信任問題

第一種方法,需要在每台運行該java程序的機器上,都做導入操作,不方便部署,因此,采用第二種方法。下面貼下該方法對應的代碼。

 

驗證可行的代碼

1)先實現驗證方法

 

 

HostnameVerifier hv = new HostnameVerifier() {
        public boolean verify(String urlHostName, SSLSession session) {
            System.out.println("Warning: URL Host: " + urlHostName + " vs. "
                               + session.getPeerHost());
            return true;
        }
    };
 
 private static void trustAllHttpsCertificates() throws Exception {
 javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
 javax.net.ssl.TrustManager tm = new miTM();
 trustAllCerts[0] = tm;
 javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
 .getInstance("SSL");
 sc.init(null, trustAllCerts, null);
 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
 .getSocketFactory());
 }

 static class miTM 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; } }

 

2)在訪問https資源前,調用

 

trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);

 

 

 

參考文檔(本文其實是將這三篇文章中,相關的內容整合到一起):

http://blog.csdn.net/mingli198611/article/details/8055261《HTTP和HTTPS詳解》

http://www.cnblogs.com/wupher/archive/2012/08/05/2623561.html《使用Keytool為JDK添加https證書信任》

http://mengyang.iteye.com/blog/575671《解決PKIX path building failed的問題》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM