添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
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),因此這邊做一個記錄方便以後找到。

「打造自己的template-建立一致性程式碼」 (13) 「Cognitive Service之Face Api」 (13) 「從 Microsoft Learn 學 Azure」 (11) azure-devops (10) 「Bot Framework V4」 (10) hadoop (9) 「證照」 (9) 「Azure DevOps」 (8) 「部落格改版學DevOps」 (8) luis (7) best practices (7) custom-vision (7) wyam (6) lab (6) apiary (6) test (6) csharp (6) 「開發工具小技巧」 (6) 「.net core 與 .net standard 實戰教學」 (6) code-review (5) code-quality (5) 「簡報」 (5) qna-maker (5) 「devdaysasia2019」 (4) api-blueprint (4) r (4) computer-vision (4) sql (4) 「net framework工程師看net core」 (4) microsoft-teams (4) asp-net-core (4) data processing (4) speech-service (4) nuget (4) 「挑選適合的 Azure 服務」 (4) visual studio team service (3) 「回顧」 (3) iis (3) autofac (3) entity framework (3) net-standard (3) ssh (3) 「微軟 MVP」 (3) postman (3) security (2) kubernetes (k8s) (2) bot (2) ml-classifier (2) netlify (2) azure-app-service (2) docker (2) continuous-delivery (2) 書評 (2) azure-iot-hub (2) refactor (2) visual studio code (1) application-insight (1) python (1) ChatGPT (1) 「az104-lab」 (1) debug (1) cost (1) 「AZ-204 在考什麼」 (1) seo (1) 「ebook」 (1) jquery (1) AI (1) ddd (1) reliability (1) ACS (1) cake (1) markdown (1) linq (1) network (1) OpenAI (1) cosmos db (1)