1. 程式人生 > >Java訪問HTTPS時證書驗證問題

Java訪問HTTPS時證書驗證問題

rar html illegal fault tle 包含 verify boolean mitm

為了盡可能避免安全問題,公司的很多系統服務都逐步https化,雖然開始過程會遇到各種問題,但趨勢不改。最完美的https應用是能實現雙向認證,客戶端用私鑰簽名用服務端公鑰加密,服務端用私鑰簽名客戶端都公鑰加密,但現實很多情況不可能讓每個客戶端都申請一個證書,因此只實現https的單項認證,即只要服務端又證書,客戶端只驗證https端證書可靠就可進行https通信。在某些情況下為了不花錢買第三方信任機構頒發都證書,客戶端在一些情況下也不做服務器端都認證,兩邊只實現htts的加密通信。最近就遇到一個問題,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)先實現驗證方法 [java] view plain copy
  1. HostnameVerifier hv = new HostnameVerifier() {
  2. public boolean verify(String urlHostName, SSLSession session) {
  3. System.out.println("Warning: URL Host: " + urlHostName + " vs. "
  4. + session.getPeerHost());
  5. return true;
  6. }
  7. };
  8. private static void trustAllHttpsCertificates() throws Exception {
  9. javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
  10. javax.net.ssl.TrustManager tm = new miTM();
  11. trustAllCerts[0] = tm;
  12. javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
  13. .getInstance("SSL");
  14. sc.init(null, trustAllCerts, null);
  15. javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
  16. .getSocketFactory());
  17. }
  18. static class miTM implements javax.net.ssl.TrustManager,
  19. javax.net.ssl.X509TrustManager {
  20. public java.security.cert.X509Certificate[] getAcceptedIssuers() {
  21. return null;
  22. }
  23. public boolean isServerTrusted(
  24. java.security.cert.X509Certificate[] certs) {
  25. return true;
  26. }
  27. public boolean isClientTrusted(
  28. java.security.cert.X509Certificate[] certs) {
  29. return true;
  30. }
  31. public void checkServerTrusted(
  32. java.security.cert.X509Certificate[] certs, String authType)
  33. throws java.security.cert.CertificateException {
  34. return;
  35. }
  36. public void checkClientTrusted(
  37. java.security.cert.X509Certificate[] certs, String authType)
  38. throws java.security.cert.CertificateException {
  39. return;
  40. }
  41. }
2)在訪問https資源前,調用 [java] view plain copy
  1. trustAllHttpsCertificates();
  2. HttpsURLConnection.setDefaultHostnameVerifier(hv);

http://blog.csdn.net/lizeyang/article/details/18983843

解決https證書驗證不通過的問題

1、報錯信息

java.security.cert.CertificateException: No name matching api.weibo.com found; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching api.weibo.com found

  原因:在調用api.weibo.com的時候,我們使用的是https的方式,正常情況下應該是使用api.weibo.com的證書,但由於某些原因,我們只能使用自己的證書,導致在驗證證書的時候,就報了這個錯誤。

  解決的辦法:忽略服務端和客戶端的證書校驗即可。java 提供的相關的類。

2、具體實現方式

  通過重寫TrustManager的checkClientTrusted(檢查客戶端證書信任)和checkServerTrusted(檢查服務端證書驗證)。

  以及HostnameVerifier的verify(校驗)方法即可取消對證書的所有驗證。

技術分享
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public final class DisableSSLCertificateCheckUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(DisableSSLCertificateCheckUtil.class);

    /**
     * Prevent instantiation of utility class.
     */

    private DisableSSLCertificateCheckUtil() {

    }

    /**
     * Disable trust checks for SSL connections.
     */

    public static void disableChecks() {
        try {
            new URL("https://0.0.0.0/").getContent();
        } catch (IOException e) {
            // This invocation will always fail, but it will register the
            // default SSL provider to the URL class.
        }
        try {
            SSLContext sslc;
            sslc = SSLContext.getInstance("TLS");
            TrustManager[] trustManagerArray = {new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }};
            sslc.init(null, trustManagerArray, null);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });
        } catch (Exception e) {
            LOGGER.error("error msg:{}", e);
            throw new IllegalArgumentException("證書校驗異常!");
        }
    }
}
技術分享

  調用方式:

DisableSSLCertificateCheckUtil.disableChecks();

  影響的範圍:將會影響整個tomcat裏面對證書的驗證。即通過tomcat裏面的其他項目雖然沒有執行這一段代碼但是也同樣會忽略證書的驗證。

  影響的時間:執行這段代碼之後的所有時間都生效。

Java訪問HTTPS時證書驗證問題