基礎連接已關閉:無法為ssl/tls安全通道建立信任關係根據驗證程序遠端憑證是無效的

基礎連接已關閉:無法為ssl/tls安全通道建立信任關係根據驗證程序遠端憑證是無效的

圖片來源:https://pixabay.com/en/despair-alone-being-alone-archetype-513528/

有時候需要在C#的程式裡面發出request和內部或者外部的服務溝通,如果內部或者外部的服務只允許https連線,而且的ssl憑證並沒有經過認證(有可能是用self signed certifcate),那麼C#會直接出錯:

基礎連接已關閉:無法為ssl/tls安全通道建立信任關係根據驗證程序遠端憑證是無效的

錯誤訊息範例畫面

system.security.authentication.authenticationexception the remote certificate is invalid according to the validation procedure

System.Net.Http.HttpRequestException: 傳送要求時發生錯誤。 ---> System.Net.WebException: 基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係。
---> System.Security.Authentication.AuthenticationException: 根據驗證程序,遠端憑證是無效的。

一般來說要解決這個問題有兩個做法:

  1. 把self sign的certificate裝到程式的機器上面並且信任那個憑證
  2. 在送出request的時候做一些特殊處理

這篇將會對於第二個做法,調整程式讓發出request遇到這種問題的時候能夠處理這種問題。

  • 問題發生原因
  • 解決方式
    • .Net Framework裡面的處理方式
    • .Net Core裡面的處理方式
  • 結語

問題發生原因

基本上合法的ssl憑證一定會是由一個可信任的機構發出,不過有時候有些服務只是要把通訊的管道加密,為了節省成本(或者其他什麼原因)而使用了self sign certificate (自我簽章的憑證)。

理論上任何會對外被呼叫到的https網站都不應該用self sign certificate,因此,從C#發出的https request(不管是用WebClient還是HttpClient)都會檢查ssl憑證是否合法。

也就是因為這個檢查導致出現錯誤。

解決方式

取決於你的target platform是什麼,有兩種解決方式:

  1. .Net Framework裡面的處理方式
  2. .Net Core裡面的處理方式

.Net Framework裡面的處理方式

基本上有個全域的事件叫做ServicePointManager.ServerCertificateValidationCallback會在ssl憑證驗證的時候被觸發,因此假設不管檢查結果是如何,都要通過的情況下,可以再系統啟動的時候呼叫:

ServicePointManager.ServerCertificateValidationCallback +=
 (sender, cert, chain, sslPolicyErrors) => true;

不過這個有點太大,因此建議還是針對性的去通過比較好,舉例來說,假設有ssl憑證的host是:problem.com,那麼可以只當host是problem.com的情況下有驗證錯誤在過,範例如下:

ServicePointManager
   .ServerCertificateValidationCallback +=
  (sender, cert, chain, sslPolicyErrors) =>
   {
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
     return true;
    }
    var request = sender as HttpWebRequest;
    if (request != null)
    {
     var result = request.RequestUri.Host == "problem.com";

     return result;
    }
    return false;
   };

程式應該還蠻好理解:

  • 如果ssl驗證沒有錯誤,直接回傳過(true)
  • 判斷request的host是不是符合我們已知有問題憑證的host - 如果是就回傳過(true)
  • 要不然都不過(false)

或許你會說幹嘛這麼麻煩,何不都過就好?這個其實是為了明確定義那些是特例可以通過,那些不可以。 要記得,這個修改是全域都有作用,換句話說所有送出的request,包含像是Xml Web Service這種reference都會吃,因此明確一點比較好。

.Net Core裡面的處理方式

在.Net Core裡面會發現沒有ServicePointManager,只有在HTTPClient 4.1的版本以上有支援可以再request裡面設定(其實.Net Framework如果用的也是HttpClient 4.1 以上也可以用這種方式):

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = 
    (httpRequestMessage, cert, cetChain, policyErrors) =>
 {   
  return true;
 };
var client = new HttpClient(handler);

因為這個設定是和某一個client有關,因此直接全部return true比較不會有問題 - 不像上面是一個全域的事件。

結語

基本上這個問題透過google就能夠找到,不過比較少有提到判斷host的部分(都是直接return true),因此這邊做一個記錄方便以後找到。


如果文章對您有幫助,就請我喝杯飲料吧

公司將一些網站改走SSL後發現有些POST取資料功能失效了
出現基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係
解法辦法

 ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;<=加入
 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;<=加入

參考來源
[faq]解決C#呼叫有ssl憑證問題的網站出現遠端憑證是無效的錯誤問題
C# HttpReques 基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係 強制跳過 TLS驗證
C# 連線 HTTPS 網站發生驗證失敗導致基礎連接已關閉

2019-07-07

C# HttpReques 基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係 強制跳過 TLS驗證

  • 7894
  • 0
  • C#
  • 2019-10-16

如提

C#  HttpReques 基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係    強制跳過 TLS驗證

參考來源

http://www.coco-in.net/thread-150342-1-1.html

https://blog.alantsai.net/posts/2017/12/csharp-ssl-remote-validation-error

可能會遇到這個畫面

基礎連接已關閉:無法為ssl/tls安全通道建立信任關係根據驗證程序遠端憑證是無效的

實際寫法

    ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

   HttpWebRequest request = HttpWebRequest.Create(targetUrl) as HttpWebRequest;
                request.Method = "GET";
            request.ContentType = "application/x-www-form-urlencoded";

            string jsondata = "";
            // 取得回應資料
            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    jsondata = sr.ReadToEnd();
                }
            }

就可以解決了

以上文章僅用紀錄資料使用.....