Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[faq]解決C#呼叫有ssl憑證問題的網站出現遠端憑證是無效的錯誤問題

2017-12-10 Sunday
image
圖片來源:https://pixabay.com/en/despair-alone-being-alone-archetype-513528/

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

image
錯誤訊息範例畫面

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遇到這種問題的時候能夠處理這種問題。

問題發生原因

基本上合法的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),因此這邊做一個記錄方便以後找到。

標籤: ,,

如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
2017-12-10 Sunday
comments powered by Disqus