首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java HttpsURLConnection 忽略 SSL 證書驗證訪問 HTTPS 接口完整代碼

     編輯:Java 綜合教學

應用場景

  • 開發/測試環境調用自簽名證書 HTTPS 接口
  • 對接第三方測試環境證書不受信任
  • 爬蟲/測試工具快速驗證 HTTPS 接口

⚠️ 生產環境嚴禁使用

忽略 SSL 證書驗證會導致 中間人攻擊 (MITM) 風險,生產環境必須配置受信任 CA 證書或導入服務器證書到信任庫。

核心原理:自定義 TrustManager 信任所有證書

通過實現 X509TrustManager 接口,空實現驗證方法,構建信任所有證書的 SSLContext,替換默認的 Socket 工廠。

完整可運行代碼

package test;

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

public class HttpsIgnoreCertDemo {

    public static void main(String[] args) {
        String targetUrl = "https://home.cnblogs.com/u/mjn0"; // 目標 HTTPS 地址
        
        HttpsURLConnection conn = null;
        try {
            // 1. 創建連接
            URL url = new URL(targetUrl);
            conn = (HttpsURLConnection) url.openConnection();
            
            // 2. 構建信任所有證書的 SSLContext
            SSLContext sslContext = createTrustAllSSLContext();
            
            // 3. 設置自定義 SocketFactory
            conn.setSSLSocketFactory(sslContext.getSocketFactory());
            
            // 4. 可選:忽略主機名驗證 (防止 HostnameVerifier 拋異常)
            conn.setHostnameVerifier((hostname, session) -> true);
            
            // 5. 發送請求並讀取響應
            printResponse(conn);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 創建信任所有證書的 SSLContext
     */
    private static SSLContext createTrustAllSSLContext() 
            throws NoSuchAlgorithmException, KeyManagementException {
        
        // 信任管理器:不驗證證書鏈
        TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {}
                
                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {}
                
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0]; // 返回空數組
                }
            }
        };

        // 初始化 SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS"); // 或 "SSL"
        sslContext.init(null, trustAllCerts, new SecureRandom());
        return sslContext;
    }

    /**
     * 讀取並打印響應內容
     */
    private static void printResponse(HttpsURLConnection conn) throws IOException {
        try (InputStream is = conn.getInputStream();
             InputStreamReader isr = new InputStreamReader(is, "UTF-8");
             BufferedReader br = new BufferedReader(isr)) {
            
            String line;
            StringBuilder response = new StringBuilder();
            while ((line = br.readLine()) != null) {
                response.append(line).append("\n");
            }
            
            System.out.println("響應狀態碼: " + conn.getResponseCode());
            System.out.println("響應內容長度: " + response.length());
            System.out.println("--- 響應體前 500 字符 ---");
            System.out.println(response.substring(0, Math.min(500, response.length())));
        }
    }
}

關鍵技術點深度解析

1. 協議版本選擇:TLS vs SSL

SSLContext.getInstance("TLS")  // 推薦:TLS 1.2/1.3,現代標準
SSLContext.getInstance("SSL")  // 不推薦:SSLv3 已棄用,存在 POODLE 漏洞

2. 主機名驗證 (HostnameVerifier)

即使信任證書,JDK 默認仍會驗證證書 CN/SAN 與請求域名是否匹配。設置:

conn.setHostnameVerifier((hostname, session) -> true); // 忽略主機名驗證

3. 全局設置 vs 單連接設置

方式代碼影響範圍線程安全
單連接conn.setSSLSocketFactory()僅當前連接安全
全局HttpsURLConnection.setDefaultSSLSocketFactory()JVM 所有 HttpsURLConnection不安全 (多線程競態)

強烈建議使用單連接設置,避免污染全局 SSL 上下文。

4. 證書鏈驗證邏輯

// 正規驗證流程 (被我們繞過)
1. 服務器發送證書鏈 (葉子證書 → 中間 CA → 根 CA)
2. 客戶端檢查:
   - 簽名算法安全性
   - 有效期 (NotBefore/NotAfter)
   - 撤銷狀態 (CRL/OCSP)
   - 域名匹配 (CN/SAN)
   - 信任錨點:根 CA 是否在信任庫中
3. 所有通過 → 建立加密通道

生產環境正確做法:證書導入信任庫

方案一:導入 JKS 信任庫 (推薦)

# 1. 導出服務器證書 (或從瀏覽器導出 .cer)
openssl s_client -connect api.example.com:443 -servername api.example.com  server.pem

# 2. 導入 JDK cacerts 或自定義信任庫
keytool -importcert -alias api-example -keystore /path/to/truststore.jks -file server.pem -storepass changeit

# 3. JVM 啟動參數指定信任庫
-Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit

方案二:代碼中加載指定信任庫

// 加載自定義 TrustStore
KeyStore trustStore = KeyStore.getInstance("JKS");
try (FileInputStream fis = new FileInputStream("/path/to/truststore.jks")) {
    trustStore.load(fis, "changeit".toCharArray());
}

// 構建基於該 TrustStore 的 TrustManagerFactory
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);

// 初始化 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());

// 使用
conn.setSSLSocketFactory(sslContext.getSocketFactory());

進階:雙向認證 (Mutual TLS / mTLS)

若服務器要求客戶端證書,需同時設置 KeyManager:


總結

  1. 開發測試:使用自定義 TrustManager 忽略驗證,代碼簡潔有效
  2. 生產環境:務必配置受信 CA 證書或導入服務器證書到信任庫
  3. 最佳實踐:單連接設置 SSLSocketFactory、顯式指定 TLS 協議、避免全局污染
  4. 雙向認證:需同時配置 KeyManager (客戶端證書) 和 TrustManager (服務器信任)

掌握 HTTPS 底層 SSL/TLS 握手機制,能從根本上解決各類證書異常、連接失敗、安全合規問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved