首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# HttpWebRequest 忽略 SSL 憑證驗證錯誤的完整解決方案

     編輯:關於C語言

問題現象

使用 HttpWebRequest 呼叫 HTTPS 服務時,拋出例外:

「基礎連接已經關閉: 無法與遠程服務器建立信任關系。」
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

根本原因

  • 伺服器憑證為自簽憑證、過期、網域名稱不符 (CN 不匹配)、根憑證不在信任存儲。
  • .NET Framework 1.1+ 預設啟用嚴格驗證 (1.0 版會忽略名稱不匹配)。
  • 常見於開發/測試環境、內部微服務、舊版系統整合。

方案一:全域忽略所有憑證錯誤 (僅限開發/測試)

// 必須在第一個 HttpWebRequest 建立前調用 (如 Application_Start、Main、靜態建構子)
System.Net.ServicePointManager.ServerCertificateValidationCallback 
    = (sender, certificate, chain, sslPolicyErrors) => true;

優點:一行解決、無需修改業務代碼。
缺點:極度不安全,中間人攻擊零防禦,**嚴禁生產環境使用**。

方案二:實作 ICertificatePolicy (舊版 API,.NET Framework 適用)

public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
{
    public bool CheckValidationResult(
        ServicePoint sp,
        System.Security.Cryptography.X509Certificates.X509Certificate cert,
        WebRequest req,
        int problem)
    {
        // problem 代碼對照:
        // 0x800B010F (CN不匹配) | 0x800B0101 (過期) | 0x800B0109 (根不受信) 等
        return true; // 無條件信任
    }
}

// 註冊 (同樣需在首次請求前)
ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

方案三:針對性忽略特定錯誤 (生產環境可接受風險)

ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) =>
{
    // 僅忽略「名稱不匹配」與「過期」,保留其他驗證 (如吊銷、用途)
    if (errors == SslPolicyErrors.RemoteCertificateNameMismatch 
        || errors == SslPolicyErrors.RemoteCertificateChainErrors) // 包含過期、根不信等
    {
        // 可加入額外邏輯:檢查憑證指紋、發行者、有效期範圍等
        return true; 
    }
    return errors == SslPolicyErrors.None;
};

方案四:正確安裝憑證 (生產環境標準做法)

  1. 取得伺服器憑證 (.cer/.crt/.pem) 或根 CA 憑證。
  2. 以管理員身分執行 mmc.exe → 檔案 → 新增/移除嵌入式管理單元 → 憑證 → 電腦帳戶 → 本機電腦。
  3. 展開 受信任的根憑證授權機構 → 憑證 → 右鍵 → 所有工作 → 匯入 → 選取憑證檔案。
  4. 或程式化匯入:
using System.Security.Cryptography.X509Certificates;

var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2(@"C:\certs\my-ca.cer"));
store.Close();

方案五:使用 HttpClient (現代 .NET Core / .NET 5+ 推薦)

// 註冊自訂驗證 (僅開發環境)
var handler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var client = new HttpClient(handler);

// 或針對性驗證
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) =>
{
    // 自訂邏輯
    return errors == SslPolicyErrors.None || errors == SslPolicyErrors.RemoteCertificateNameMismatch;
};
var client = new HttpClient(handler);

常見錯誤代碼對照表

代碼 (Hex)常數名稱含義
0x800B0101CERT_E_EXPIRED憑證過期
0x800B0109CERT_E_UNTRUSTEDROOT根憑證不受信任
0x800B010FCERT_E_CN_NO_MATCH主體名稱 (CN) 與主機名不符
0x800B010CCERT_E_REVOKED憑證已吊銷
0x800B010ECERT_E_REVOCATION_FAILURE吊銷檢查失敗 (離線/無法連線 CRL/OCSP)

疑難排解清單

  1. ✅ 確認伺服器時間與客戶端時間差 < 5 分鐘 (影響有效期驗證)。
  2. ✅ 瀏覽器直接訪問該 HTTPS URL,檢視憑證詳情 (是否自簽、過期、CN 匹配)。
  3. ✅ 使用 openssl s_client -connect host:port -showcerts 抓取憑證鏈分析。
  4. ✅ 若伺服器需客戶端憑證 (雙向 TLS),確認 HttpWebRequest.ClientCertificates 已正確加載含私鑰的 .pfx。
  5. ✅ 檢查 Proxy/Firewall 是否截斷 TLS 握手 (抓包看 Client Hello / Server Hello)。
  6. ✅ .NET Framework 版本差異:4.0+ 預設支援 TLS 1.1/1.2,舊版需 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;。

安全性警示

⚠️ 生產環境嚴禁使用方案一/二/三 (無條件信任)。
正確做法:佈署有效憑證 (Let's Encrypt 免費、內部 PKI、商業 CA),或將自簽/內部 CA 根憑證加入機器信任存儲。僅在隔離測試環境、無法控制伺服器憑證的舊系統整合中,謹慎使用針對性忽略 (方案三) 並記錄審計日誌。

總結

SSL/TLS 信任失敗本質是憑證鏈驗證不通過。根治之道是修正憑證部署;臨時繞過需權衡風險,優先使用 ServerCertificateValidationCallback 針對性放行,並嚴格限制範圍。現代專案請遷移至 HttpClient,享受更優雅的 API 與跨平台一致性。

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