有時候需要在C#的程式裡面發出request和內部或者外部的服務溝通,如果內部或者外部的服務只允許https連線,而且的ssl憑證並沒有經過認證(有可能是用self signed certifcate),那麼C#會直接出錯: system.security.authentication.authenticationexception the remote certificate is invalid according to the
validation procedure System.Net.Http.HttpRequestException: 傳送要求時發生錯誤。 ---> System.Net.WebException: 基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係。 一般來說要解決這個問題有兩個做法: 這篇將會對於第二個做法,調整程式讓發出request遇到這種問題的時候能夠處理這種問題。
圖片來源://pixabay.com/en/despair-alone-being-alone-archetype-513528/
錯誤訊息範例畫面
---> System.Security.Authentication.AuthenticationException: 根據驗證程序,遠端憑證是無效的。
問題發生原因
基本上合法的ssl憑證一定會是由一個可信任的機構發出,不過有時候有些服務只是要把通訊的管道加密,為了節省成本(或者其他什麼原因)而使用了self sign certificate (自我簽章的憑證)。
理論上任何會對外被呼叫到的https網站都不應該用self sign certificate,因此,從C#發出的https request(不管是用WebClient還是HttpClient)都會檢查ssl憑證是否合法。
也就是因為這個檢查導致出現錯誤。
解決方式
取決於你的target platform是什麼,有兩種解決方式:
- .Net Framework裡面的處理方式
- .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 安全通道建立信任關係
解法辦法
參考來源
[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驗證
參考來源
//www.coco-in.net/thread-150342-1-1.html
//blog.alantsai.net/posts/2017/12/csharp-ssl-remote-validation-error
可能會遇到這個畫面
實際寫法
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();
}
}
就可以解決了
以上文章僅用紀錄資料使用.....