エラーメッセージ
Received an unexpected EOF or 0 bytes from the transport stream.
別の表記
- System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
HttpClient はこれを「The SSL connection could not be established, see inner exception.」でラップします。.NET Framework アプリケーションのログには、同じ状況が「Authentication failed because the remote party has closed the transport stream.」としてよく現れます。
.NET SslStream / HttpClient — IOException: the peer closed the connection during the TLS handshake
このエラーの意味
SslStream が TLS ハンドシェイクを行っている最中に、リモート側(または途中の機器)が TLS アラートを送らずに TCP 接続を閉じました。.NET には、ストリームが閉じられたこと以外に報告できる理由がありません。通常は、サーバーが何も通知せずにハンドシェイクを拒否したか、プロキシやファイアウォールが接続を切断したかのどちらかです。
発生する理由
受け入れないハンドシェイクをサーバーが切断している
よくある共通のプロトコルバージョンや暗号がない、SNI の名前がないか不明である、またはクライアントが提示しなかったものをサーバーが要求している状況で、サーバーがアラートを送らずに接続を閉じるケースです。
プロキシやファイアウォールが接続を切断している
よくあるプロキシが必須のネットワーク、TLS をフィルタリングするファイアウォール、VPN クライアントは、ブロックした接続を何も通知せずに終了させます。
古い TLS バージョンしか提示しない .NET Framework アプリケーション
ときどきOS 既定の TLS 設定を使っていない .NET Framework 4.5 以前を対象とするアプリケーションは、TLS 1.0 しか提示しないことがあり、最新のサーバーはこれを切断します。
修正方法
- 1.
同じマシンからハンドシェイクをテストする
openssl s_client でも切断される場合、問題はネットワークかサーバーにあります。成功する場合は、プロトコルバージョンと SNI を .NET クライアントと比較してください。
bash # Does a plain TLS handshake from the same host work? openssl s_client -connect api.example.com:443 -servername api.example.com -brief </dev/null openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 -brief </dev/null - 2.
必須のプロキシを使う
ネットワークが直接接続を許可していない場合は、ハンドラーで(または Linux では HTTPS_PROXY で)プロキシを設定してください。
csharp // Use the corporate proxy explicitly (HttpClient also honours HTTPS_PROXY / system settings) var handler = new SocketsHttpHandler { Proxy = new WebProxy("http://proxy.example.net:3128"), UseProxy = true }; using var client = new HttpClient(handler); - 3.
.NET Framework:TLS 1.2 を有効にする
4.7 以降をターゲットにして OS にプロトコルを選ばせるか、それができるまでの間は ServicePointManager.SecurityProtocol に TLS 1.2 を追加してください。
csharp // .NET Framework 4.x apps compiled for old targets may still offer only TLS 1.0: // target 4.7+ (OS defaults) or, as a stop-gap, enable TLS 1.2 explicitly System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;