【问题标题】:WebRequest on .NET Core 3.0 fails the second time due to SSL handshake exception由于 SSL 握手异常,.NET Core 3.0 上的 WebRequest 第二次失败
【发布时间】:2020-03-15 04:05:58
【问题描述】:

问题是下面的代码如果你运行一次就会成功,但如果你在程序的同一生命周期内运行两次就会失败。

var request = (HttpWebRequest)WebRequest.Create("https://google.com");
var response = (HttpWebResponse)await request.GetResponseAsync();

我有 .NET Core 3.0 和 SDK 版本 3.0.100。

转载:

  1. dotnet new console -n test-sslcd test-ssl.
  2. 打开Program.cs。
  3. 将内容替换为:
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;

namespace test_ssl
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await CallGoogle();
            await CallGoogle();
        }

        private static async Task CallGoogle()
        {
            var request = (HttpWebRequest)WebRequest.Create("https://google.com");
            var response = (HttpWebResponse)await request.GetResponseAsync();
            var jsonResponse = new StreamReader(response.GetResponseStream()).ReadToEnd();
            Console.WriteLine(jsonResponse);
        }
    }
}
  1. dotnet run。

这将崩溃,但异常:

Unhandled exception. System.Net.WebException: The SSL connection could not be established, see inner exception. The handshake failed due to an unexpected packet format.
 ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.IO.IOException: The handshake failed due to an unexpected packet format.
   at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.Security.SslStream.ThrowIfExceptional()
   at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
   at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
   at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at System.Net.HttpWebRequest.SendRequest()
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Net.WebRequest.<>c.<GetResponseAsync>b__68_2(IAsyncResult iar)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
   at test_ssl.Program.CallGoogle() in C:\Users\me\source\Workspaces\tests\test-ssl\Program.cs:line 19
   at test_ssl.Program.Main(String[] args) in C:\Users\me\source\Workspaces\tests\test-ssl\Program.cs:line 13
   at test_ssl.Program.<Main>(String[] args)

为什么第二次会失败?

这个问题也发布在 github 上的 .NET Core 中,因为它似乎与此相关:https://github.com/dotnet/core/issues/3847

进一步测试以缩小问题范围

.NET Framework 4.8 - 工作中

使用 .NET Framework 4.8 控制台应用程序 确实有效。所以使用原始的Program.cs 文件在这里有效。似乎真正的问题在于 .NET Core 3.0

.NET Core 3.0 在 Docker - 工作中

docker run --rm -it mcr.microsoft.com/dotnet/core/sdk:3.0
dotnet new console -n test-ssl
cd test-ssl
// Replace content of Program.cs again.
dotnet run
// Everything works.

这让我相信我的机器上有些东西坏了,也可能不是防火墙问题,因为最终 docker 容器将通过与我的计算机相同的防火墙到达谷歌。

Powershell 测试 - 工作

test-ssl.ps1

Invoke-WebRequest -Uri 'https://google.com'
Invoke-WebRequest -Uri 'https://google.com'

HttpClient .NET Core 3.0 - 不工作

使用HttpClient 会出现同样的错误。所以用下面的替换CallGoogle的内容是行不通的:

var client = new HttpClient();
await client.GetStringAsync("https://google.com");

【问题讨论】:

  • 错误显示The handshake failed due to an unexpected packet format.。您不能重用 WebRequest 对象,它们代表 single 请求。你为什么不使用HttpClient?没有理由继续使用 HttpWebRequest
  • 特别是如果您使用 .NET Core 3.0,则没有理由仍然使用 HttpWebRequest。创建客户端一次,例如var client=new HttpClient("http://www.google.com"); 并从多个线程调用GetStringAsync() 任意次数
  • 在CallGoogle 中将WebRequest 替换为var client = new HttpClient(); await client.GetStringAsync("https://google.com"); 仍然给我同样的错误。但是,如果我有一个 .NET Framework 4.8 控制台应用程序,则原始代码可以工作。
  • 无复制。 HttpClient 在 .NET Core 3.0 和所有以前的版本中运行良好。如果没有,任何使用它调用 HTTP API 的人都会注意到这个问题。
  • 您是否位于可能正在运行的防火墙后面?你使用像 Fiddler 这样的调试代理吗? docker run 有什么意义?您是否在 inside 容器中运行此代码?

标签: c# ssl .net-core tls1.2 .net-core-3.0


【解决方案1】:

似乎系统一定是在某种程度上损坏了,因为它基本上在其他任何地方都可以工作。有一台可以工作的新电脑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 2011-04-16
    • 1970-01-01
    • 2017-10-24
    相关资源
    最近更新 更多