Mono上的SslStream没有完全关闭

时间:2017-02-02 12:51:07

标签: c# linux ssl xamarin mono

我目前正在使用我们设计用于与Xamarin移动应用程序通信的服务器应用程序。我们正在使用一个与TcpClient建立连接的旧消息库,并保持连接打开(每隔3秒发送一次心跳消息)。我们通过使用TcpClient打包SslStream流,将SSL添加到库中。我们在Windows上运行服务器应用程序并且运行良好,但我们的最终目标是BeagleBoneBlack上的Mono

但是,当我们关闭移动应用程序端的流和客户端,然后尝试重新启动新连接时,SslStream.AuthenticateAsServer(...)将无法在服务器上完成。但是,如果我完全关闭移动应用程序,服务器将抛出异常。此时,我重新打开应用程序,可以毫无问题地重新连接。

所以看起来应用程序或服务器端都没有关闭低级别的东西。奇怪的是,当服务器在Windows上运行时,我在两者上运行完全相同的代码并且我没有问题。

这是关闭/处理流

的代码
public async Task Disconnect()
{
    if (!UseAsync)
    {
        semaphore.Wait();
    }
    try
    {
        if (UseSSL)
        {
            SslStream?.Close();
        }
        Client?.GetStream()?.Close();
        Client?.Close();
    }
    catch (Exception ex) // Assuming we had an exception from trying to close the sslstream
    {
        logger.Error(ex, "Did not close/dispose correctly: {0}", ex.ToString());
    }
    finally
    {
        SslStream = null;
        Client = null;
        if (!UseAsync)
        {
            semaphore.Release();
        }
    }
}

编辑:它不应该是重要的,因为问题似乎在服务器某处,而客户端和服务器的ssl代码几乎相同,但万一有人问,这里是客户端断开代码

public async Task Disconnect()
{
    try
    {
        if (UseSSL)
        {
            _sslStream?.Close();
        }
        Client?.GetStream()?.Close();
        Client?.Close();
    }
    catch // Assuming we had an exception from trying to close the sslstream
    {
        // Ignore exceptions since we've already closed them
    }
    finally
    {
        _sslStream = null;
        Client = null;
    }
}

修改2

还应该注意的是,我发现至少有一个bug report看起来像我正在处理的问题一样。从这个错误报告中可以看出它从未得到解决,但我发现其他报告似乎反映了单声道框架中的类似问题并且已经解决。另外,我添加了一些代码来发送一些" dummy"在连接ant之后来自客户端的数据似乎没有任何影响。

编辑3:我最终在客户端收到此例外

System.IO.IOException: The authentication or decryption has failed. ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: The authentication or decryption has failed.
at Mono.Security.Protocol.Tls.RecordProtocol.EndReceiveRecord (System.IAsyncResult asyncResult) [0x0003a] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs:430 
at Mono.Security.Protocol.Tls.SslClientStream.SafeEndReceiveRecord (System.IAsyncResult ar, System.Boolean ignoreEmpty) [0x00000] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs:256 
at Mono.Security.Protocol.Tls.SslClientStream.NegotiateAsyncWorker (System.IAsyncResult result) [0x00360] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs:533 

1 个答案:

答案 0 :(得分:0)

嗯......我几乎感到愚蠢地回答这个问题,但我觉得其他人可能会因为同样的无知而最终陷入困境,所以希望这可以帮助那些拥有类似架构的人。

发生的事情是我创建了一个自签名证书并将其放在我的项目文件夹中,因此我的所有"服务器上都使用了相同的证书。实例。发生的事情是,由于SSL会话缓存的一些内部缓存,在连接到一个设备之后,它会缓存其信息,然后在下次连接时重新使用该信息。结果是,由于自签名证书对所有设备使用相同的主机名,一旦它在连接到设备A后尝试重新连接到设备B,它将尝试重新使用设备A的缓存信息(据我所知)最初没有意义的原因是我可以反复连接和断开连接到多个不同的Windows服务器,但只有在连接到Mono服务器时我才能看到这个问题。因此,它似乎仍然是Windows或Mono的SSLStream实现中的一个错误(因为,在一个完美的世界中,它们是相同的),但不幸的是我没有时间挖掘解编译源代码以找到它。坦率地说,它可能并不重要,因为我正在做的事情就是打破了SSL连接的整个概念。

最终,我创建了一个函数,使用Mono.Security以编程方式为每个设备生成一个唯一的证书,然后提供一种机制来为客户端提供唯一的主机名(即使客户端直接连接到IP地址)。 / p>