前言
今天同事詢問透過 .NET Core HttpClient 去 Call Https 網頁時,會噴 The SSL connection could not be established Win32Exception (0x80090326): 接收到的訊息超出預期或格式不正確 的錯誤,詳細如下,
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
—> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
—> System.ComponentModel.Win32Exception (0x80090326): 接收到的訊息超出預期或格式不正確。
— End of inner exception stack trace —
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
研究
在網路上有找到TLS handshake failures are not clear with SChannel這篇有提到似乎是因為 Cipher 的問題。
於是使用以下的 Console 程式在本機上測試卻沒有任何問題,
1 | static async Task Main(string[] args) |
拿到正式機去執行就真的發生一樣錯誤。
於是到SSL Server Test測試一下該網站 Support 幾種 Cipher ,結果只 Support 3 種 384 的。
於是透過IIS Crypto Tool 將本機 384 Bits 的 Cipher 都取消,重開機再執行測試程式真的發生一樣的錯誤。
.NET Core 的程式會噴以下的錯誤,
System.Net.Http.HttpRequestException
HResult=0x80131501
Message=The SSL connection could not be established, see inner exception.
Source=System.Net.Http
StackTrace:
at System.Net.Http.ConnectHelper.
內部例外狀況 1:
AuthenticationException: Authentication failed, see inner exception.
內部例外狀況 2:
Win32Exception: 接收到的訊息超出預期或格式不正確。
.NET Framework 的程式,錯誤訊息如下,
System.Net.Http.HttpRequestException
HResult=0x80131500
Message=傳送要求時發生錯誤。
Source=mscorlib
StackTrace:
於 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
內部例外狀況 1:
WebException: 要求已經中止: 無法建立 SSL/TLS 的安全通道。
而透過 Wireshark 來看,錯誤是Handshake Failure
即然猜到了真正原因,於是就將 Server 需要的 Cipher 再加進去。
重開機後,再執行程式就沒有問題了,透過 Wireshark 來看,在 Client Hello 送上去的 Cipher 就有包含 Server 允許的 Cipher ,也正常出現 Server Hello
後來同事使用IIS Crypto Tool在正式機上檢視,果然沒有那些 384 的 Cipher。
WebException: 基礎連接已關閉: 傳送時發生未預期的錯誤。 遠端主機已強制關閉一個現存的連線
有時如果使用不允許的通訊協定,例如 Server 只允許 TLSv1.2 ,Client 給 TLSv1 就會產生以下的錯誤,
System.Net.Http.HttpRequestException
HResult=0x80131500
Message=傳送要求時發生錯誤。
內部例外狀況 1:
WebException: 基礎連接已關閉: 傳送時發生未預期的錯誤。
內部例外狀況 2:
IOException: 無法從傳輸連接讀取資料: 遠端主機已強制關閉一個現存的連線。。
內部例外狀況 3:
SocketException: 遠端主機已強制關閉一個現存的連線。
而透過 Wireshark 來看,就可以看到 GG 在 Reset,因為它的 Client Hello 走的是 TLSV1
這錯大部份出現在 .NET Framework 身上,因為 .NET Framework 預設是 TLSV1.0 。
所以程式要加上
1 | System.Net.ServicePointManager.SecurityProtocol = |
Wireshark 使用 Filter
透過 Wireshark 來錄封包時,有時資料太多不好找,這時可以使用 Filter 的功能,例如我本機是 10.211.55.3 ,而 Server 是 54.238.55.188
就可以下以下的 Filter 來看這 2 邊的資訊,
(ip.src == 54.238.55.188 or ip.src == 10.211.55.3 ) and (ip.dst == 54.238.55.188 or ip.dst == 10.211.55.3)
以上是最近遇到程式呼叫 https 遇到問題的整理,希望對大家有幫助。
如果是在開發機上使用自簽憑證則可以參考Allowing Untrusted SSL Certificates with HttpClient
- 註: 最近客戶端的程式(Java)在呼叫 IIS Https 網站時,會發生 RESET 的狀況,後來客戶發現是因為 IIS 在 https 繫結設定時,主機名稱欄位的下方有一個「需要伺服器名稱指示(N)」有勾起來,導致客戶的 Java 程式呼叫會失敗。 如果以上的方式測過還是會發生 RESET 的問題,可以試著將不要勾選 「需要伺服器名稱指示(N)」 試試看哦 ! 感謝同事 Geo, Tina 的分享。
參考資訊
TLS handshake failures are not clear with SChannel
SSL Server Test
IIS Crypto Tool
Allowing Untrusted SSL Certificates with HttpClient