故障描述
昨天晚上在生產環境的某臺計算機遇到了訪問第三方應用報“未能創建 SSL/TLS 安全通道”的例外,開發的同事重新寫了兩個命令控制臺程式(.net framework 4.5 和 .netcore 3.1),問題可以100%重現,同樣的代碼在本地或者其它服務器上運行,可以正常使用,更為奇怪的是,同事使用 curl 工具或者 Python 寫的測驗代碼竟然都可以正常運行,
環境描述
作業系統: windows server 2016 Datecenter (Azure標準鏡像)
Host: China Azure
.Net Runtime: .net framework 4.7.2 和 .netcore 3.1
Target Framework:.net framework 4.5.1 和 .netcore 3.1均可以重現
測驗程式核心代碼(.net framework 4.5.1):
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
HttpWebResponse res = (HttpWebResponse)req.GetResponse()
分析
先不考慮 curl 工具或者 Python 寫的測驗代碼為什么可以正常運行,首先還是要先抓取服務器上的網路包進行分析,抓取后發現在Client Hello之后出現了Encrypted Alert,

回顧一下SSL/TLS握手流程(網圖侵刪):

在client hello的時候提供了 version、cipher suites等資訊,然后繼續看一下網路抓包發現錯誤代碼40(十六進制28轉十進制)

查找到錯誤代碼解釋:Indicates that the sender was unable to negotiate an acceptable set of security parameters given the options available. This is a fatal error.
(SSL/TLS Alert Protocol and the Alert Codes )
第一個引數02表示 AlertLevel,第二個引數28(十進制的40)表示 AlertDescription,那么問題因該出在client hello的時候傳入的引數上了,比較常見的是version問題,但是代碼里面已經明確宣告了使用 TLS 1.2 的版本,而且在其它環境運行也正常說明 TLS 的版本不會有問題,這時候查到一篇文章(How to fix the SSL / TLS handshake failed error)對我起了很大作用,按順序我開始排查Encryption suite mismatch問題,
因為對方網站可以被外網訪問到,所以我考慮使用ssllabs網站的工具進行一下檢測,生成一份SSL Report,這里可以看到目標網站支持哪些 Cipher Suites,

然后發現抓取的網路包里面client hello時沒有匹配的 Cipher Suites!

然后檢查本地的成功的請求和服務器上使用 curl 或者 Python 寫的測驗代碼的請求,發現在這一步Cipher Suites的差異很大,下圖是使用 curl 的網路包情況,

這個時候網上了一下 .Net 程式如何設定Cipher Suites,找到一個記錄(issues 22507),發現可以通過以下方式指定,
var sslOptions = new SslClientAuthenticationOptions {
CipherSuitesPolicy = new CipherSuitesPolicy(new List<TlsCipherSuite>{
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 })
};
var socketsHttpHandler = new SocketsHttpHandler { SslOptions = sslOptions };
var httpClient = new HttpClient(socketsHttpHandler, true);
httpClient.GetAsync(url).Wait();
不過實際的程式使用的是HttpWebRequest(.net framework 4.5.1)而且其它環境是可以正常使用的,并沒有宣告過Cipher Suites,所以又發現 The build-in classes HttpWebRequest and the sort all uses Windows native SChannel to implement SSL encryption, therefore has no programmatic way to control the cipher suite list. (原文),所以開始考慮 OS 配置的問題,Windows Server 2016 Cipher Suite list 我沒有查到,但可以參考其它版本的說明(Windows2022)和 How to deploy custom cipher suite ordering in Windows Server 2016 的文章,
實際上我沒有擔心過windows server 2016不支持相應Cipher Suites的問題,最簡單的證明就是其它服務器(同OS版本)是可以正常使用的,所以這臺機器應該是進行了特殊的配置,查了一下如何配置(How to Update Your Windows Server Cipher Suite for Better Security),然后打開組策略發現果然被人修改過了,

先恢復成默認值(未配置),再次運行測驗程式,通過?,
感言
從發現故障到找到原因,說起來很簡單,但實際用了將近四個小時,主要的問題就是基礎知識不牢固,SSL的流程原理什么的都是重新學習一遍,大量知識都是現查,甚至一開始方向都沒有頭緒,作業了十幾年了,越發覺得基礎知識的重要性,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/303284.html
標籤:其他
上一篇:系統架構設計師教程-學習-記錄(4)計算機網路基礎知識(3)處理機與行程管理(一)行程的定義及其分類、行程的狀態轉換與控制
