【问题标题】:ClientWebSocket on Linux throws AuthenticationException (SSL)Linux 上的 ClientWebSocket 抛出 AuthenticationException (SSL)
【发布时间】:2017-08-15 11:04:32
【问题描述】:

我在 Windows 上运行以下 websocket 客户端代码,一切正常 - 正如预期的那样。但是,如果为linux-arm 发布代码并复制到RaspberryPi3(在Raspian 下运行),它将以AuthenticationException 结束。

csproj 文件内容:

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
    <PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
  </ItemGroup>

连接尝试:(抛出异常的点)

private readonly ClientWebSocket _socket;

public ApiConnection()
{
    _socket = new ClientWebSocket();
}

public async Task Connect()
{
    // the uri is like: wss://example.com/ws
    await _socket.ConnectAsync(new Uri(_settings.WebSocketUrl), CancellationToken.None);

    if (_socket.State == WebSocketState.Open)
        Console.WriteLine("connected.");
}

异常堆栈:

System.Net.WebSockets.WebSocketException (0x80004005): Unable to connect to the remote server ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
         at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
      --- End of stack trace from previous location where exception was thrown ---
         at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
         at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
         at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
         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.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
         at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
         at System.Net.WebSockets.WebSocketHandle.<ConnectAsyncCore>d__24.MoveNext()
         at System.Net.WebSockets.WebSocketHandle.<ConnectAsyncCore>d__24.MoveNext()
      --- End of stack trace from previous location where exception was thrown ---
         at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
         at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
         at System.Net.WebSockets.ClientWebSocket.<ConnectAsyncCore>d__16.MoveNext()
      --- End of stack trace from previous location where exception was thrown ---
         at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
         at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
         at System.Runtime.CompilerServices.TaskAwaiter.GetResult()

目标 websocket 服务器在 Ubuntu 上的 nginx 代理后面运行。我认为问题取决于客户端,因为如果代码在 Windows 上执行,一切正常。

我还尝试将 CA 证书导入 Raspians“证书存储”。没有运气。

更新:
http 连接 (ws://) 也适用于 linux。看来,WebSocketClient 不信任我的 LetsEncrypt 证书?

【问题讨论】:

  • 您的客户端拒绝来自服务器的证书。一个建议是添加证书回调并记录证书错误详细信息。这可能会提供有关证书为何被拒绝的更多详细信息。
  • @Subbu 注册此类回调的最佳方式是什么? ServicePointManager 在 .NET Core 上不可用
  • @senz LetsEncrypt 不受您的设备信任,此类免费认证仅适用于主流浏览器。这与windows或linux无关。您必须在证书存储中安装 LetsEncrypt 的 ROOT CA。

标签: c# linux websocket .net-core


【解决方案1】:

当浏览器/客户端不信任服务器向其抛出的 SSL 证书时,就会发生这种情况。

要进行测试,请在浏览器中的同一站点上加载相同的 url / url,您应该会收到警告。

证书问题解决后,警告将消失。

解决 SSL 证书问题的确切过程取决于很多事情,例如...

操作系统、Web 服务器、证书颁发机构、证书提供者门户,因此这里的任何人几乎不可能向您提供有关修复证书问题的具体信息,但这就是说...

然而,在 SE 网络上有一些通用的建议...

https://unix.stackexchange.com/questions/90450/adding-a-self-signed-certificate-to-the-trusted-list

https://unix.stackexchange.com/questions/17748/trust-a-self-signed-pem-certificate

在您的情况下,由于 rasbian 基于 debian,一些标准的 debian 建议可能会有所帮助...

在 Debian 中,证书存储区位于 /etc/ssl/certs/。该目录默认包含一系列符号链接,指向由 ca-certificates 包安装的证书(包括由 c_rehash(1) 生成的所需符号链接)和一个 ca-certificates.crt,它是所有这些证书的串联。由 update-ca-certificates(8) 命令管理的所有内容,该命令负责更新符号链接和 ca-certificates.crt 文件。

添加新的 (CA) 证书到 stash 非常简单,因为 update-ca-certificates(8) 也在 /usr/local/share/ca-certificates/ 中查找文件,管理员只需将此目录中 PEM 格式的新证书(扩展名为 .crt)并以 root 身份运行 update-ca-certificates(8)。系统上的所有应用程序(wget,...)现在都应该信任它。

另一种可能的解决方案可能是“我相信我的代码不会请求错误的 url,所以我会忽略 SSL 证书错误”,你可以用这样的方法来做......

C# Ignore certificate errors?

...但这并不理想,至少它可以让您解决问题,直到您可以解决问题,最坏的情况您仍然可以检查,但通过编写自己的检查而不是一揽子返回 true。

最后一点:

我经常发现,无论使用什么操作系统,在测试/检查之间执行一两次重启这样简单的操作都可以清除您通常不会认为是问题的问题。

【讨论】:

    【解决方案2】:

    在 Windows 上验证的证书不一定在 Linux 上验证。 每个操作系统都使用不同的证书和不同的方法来验证它们,此外还有一些 Linux 已知的证书,Windows 不支持这些证书。

    可能存在这样一种情况,Windows 可以识别您的 LetsEncrypt 证书,但 Linux 无法识别,因此抛出 AuthenticationException 异常并明确说明

    “根据验证程序,远程证书无效”

    意味着 Linux 尝试验证证书,但失败了,因为它根本无法被 Linux 识别,但您的 Windows 识别它并按预期行事。

    我不太了解哪些证书可以在哪些 Linux 上运行,但我建议研究这个东西以找到一种使用证书的方法,Windows 和 Linux 都可以识别、验证和工作和。

    【讨论】:

    • 在 HttpClient 的情况下,证书得到验证。那么linux识别应该没问题吧?这个错误可以依赖_clientWebSocket.Options.ClientCertificate吗?这是默认的null,但是当我创建一个空的 X509 证书时,我的问题中的错误消息变成了NullReferenceException
    • 错误是从一开始就是客户端的web socket,windows和Linux验证它们都不同,而HttpCilent验证都是一样的,对Linux或windows的Http调用没有区别,但是有套接字之间的区别以及它在 Linux 和 Windows 中的工作方式。
    【解决方案3】:

    我最近尝试了类似的东西(尽管我使用 Mono 而不是 .Net Core),在我的情况下,只是 Raspberry 上的系统时间减少了几天(!),因此在证书之外运行“有效期自”到“有效期至”时间戳。如果 Raspi 没有通过 NTP 同步时间的 Internet 连接,就会发生这种情况。覆盆子不包含带缓冲电池的硬件时钟,因此它们在未通电时会丢失时间。

    第一步是登录树莓派并运行date,查看系统时钟是否正确。

    如果这是您的问题,您有几个可能的解决方法:

    • 启用对 Raspberry Pi 的 Internet 访问,以便它可以通过 NTP 同步时钟
    • 自己设置正确的时间,然后始终保持 Raspi 开机,确保每次开机时手动设置时间
    • 安装一个硬件时钟(3 - 5 美元,如果按照说明操作,大约需要十分钟的工作时间),然后解决问题(只要电池能用)

    另一个想法可能是检查 .Net Core 期望安装 CA 证书的位置。至少使用 Mono 这与 Linux 的默认设置不同。我使用X509Store C# API 来安装证书,而不是 (Debian-)Linux 系统工具。

    【讨论】:

    • 如果是工作场所,不幸的是他不能使用第三个选项,除非他可以在公司产品上安装组件,这不太可能......
    • pi 上的时钟工作正常。在HttpClient 的用例中,证书有效。所以这似乎不是位置问题。
    猜你喜欢
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    • 2014-10-23
    • 1970-01-01
    • 2017-11-12
    • 2011-12-09
    相关资源
    最近更新 更多