【问题标题】:C# SSLStream read, Stream read tcpclientC# SSLStream 读取,Stream 读取 tcpclient
【发布时间】:2015-08-15 13:05:26
【问题描述】:

我正在编写一个需要通过 SSL/TLS 进行通信的应用程序。

当我使用以下代码时,设备连接,但我总是读取 0 个字节:

        byte[] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;

        do
        {
            // Read the client's test message.
            bytes = sslStream.Read(buffer, 0, buffer.Length);

            sslStream.Flush();

            // Use Decoder class to convert from bytes to UTF8 
            // in case a character spans two buffers.
            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);

当我使用此代码时,我能够读取字节但显然无法解码它们。此代码读取了正确的字节数,SSl 出了什么问题,我无法取回任何字节?

            var client = server.AcceptTcpClient();
            Console.WriteLine("Connected!");

            // Get a stream object for reading and writing
            var stream = client.GetStream();

            // Loop to receive all the data sent by the client. 
            while (stream.Read(bytes, 0, bytes.Length) != 0)
            {
                var base64 = Convert.ToBase64String(bytes);
                Console.WriteLine(base64);

更大的示例,我认为这是来自 MSDN:

    static void ProcessClient(TcpClient client)
    {
        // A client has connected. Create the  
        // SslStream using the client's network stream.
        SslStream sslStream = new SslStream(client.GetStream(), false);
        // Authenticate the server but don't require the client to authenticate. 
        try
        {
            sslStream.AuthenticateAsServer(serverCertificate,
                false, SslProtocols.Tls, true);
            // Display the properties and settings for the authenticated stream.
            DisplaySecurityLevel(sslStream);
            DisplaySecurityServices(sslStream);
            DisplayCertificateInformation(sslStream);
            DisplayStreamProperties(sslStream);

            // Set timeouts for the read and write to 5 seconds.
            sslStream.ReadTimeout = 5000;
            sslStream.WriteTimeout = 5000;
            // Read a message from the client.   
            Console.WriteLine("Waiting for client message...");
            string messageData = ReadMessage(sslStream);
            Console.WriteLine("Received: {0}", messageData);

            // Write a message to the client. 
            byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
            Console.WriteLine("Sending hello message.");
            sslStream.Write(message);
        }
        catch (AuthenticationException e)
        {
            Console.WriteLine("Exception: {0}", e.Message);
            if (e.InnerException != null)
            {
                Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
            }
            Console.WriteLine("Authentication failed - closing the connection.");
            sslStream.Close();
            client.Close();
            return;
        }
        finally
        {
            // The client stream will be closed with the sslStream 
            // because we specified this behavior when creating 
            // the sslStream.
            sslStream.Close();
            client.Close();
        }
    }

    static string ReadMessage(SslStream sslStream)
    {
        // Read the  message sent by the client. 
        // The client signals the end of the message using the 
        // "<EOF>" marker.
        byte[] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;

        do
        {
            // Read the client's test message.
            bytes = sslStream.Read(buffer, 0, buffer.Length);

            sslStream.Flush();

            // Use Decoder class to convert from bytes to UTF8 
            // in case a character spans two buffers.
            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);
            // Check for EOF or an empty message. 
            if (messageData.ToString().IndexOf("<EOF>") != -1)
            {
                break;
            }
        } while (bytes != 0);

        return messageData.ToString();
    }

【问题讨论】:

  • 显然 sslStream 变量提供的流发生了一些事情。由于您的问题没有显示您是如何设置 sslStream 的,所以我只能说 ;)
  • 我添加了更多代码,这只是我在网上找到的许多示例中的一个,它们都做同样的事情。
  • 好吧,复制'n'粘贴示例然后希望一切都能神奇地正常工作总是一个坏主意。请记住,在 MSDN 等文档中给出的代码示例并不意味着是完整、健壮的代码。它们旨在说明或解释一些东西以帮助您了解如何使用特定的类/方法/等...我建议尝试理解示例中给出的代码并进行一些调试找出真正发生的事情。您只是猜测代码读取的是 0 字节,还是您如何确认确实如此? (续...)
  • 也许代码根本没有从流中读取,但由于一些异常/错误之前退出了......
  • 我想知道代码是否没有使用流,我似乎无法捕获任何异常。我知道代码返回 0 字节或超时,因为我已经在调试器中运行它并查看了变量。我还运行了wireshark,可以看到数据流中有307字节,这是我使用NetworkStream时得到的确切字节数。我只是想了解为什么我无法从 SSLStream 获得任何东西。

标签: c# .net ssl


【解决方案1】:

我最终确实解决了这个问题,并想为大家发布答案。证书是自签名的,因此“无效”,但这在设备上很常见。我必须创建一个始终返回 true 的验证回调。

我忘记了我在哪里找到了这段代码,但我认为它是 MSDN

    // The following method is invoked by the RemoteCertificateValidationDelegate.
    private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        //DisplayCertificateInformation((SslStream)sender);

        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        Debug.WriteLine("Certificate error: {0}", sslPolicyErrors);

        // Allow this client to communicate with unauthenticated servers.
        return true;
    }

我最终扩展了 TcpClient 类:

public class TlsTcpClient : TcpClient
{
    public TlsTcpClient()
    {

    }

    public TlsTcpClient(string ip, int port) : base(ip, port)
    {

    }

    public new SslStream GetStream()
    {
        var sslStream = new SslStream(base.GetStream(),
            true,
            ValidateServerCertificate,
            null);

        try
        {
            sslStream.AuthenticateAsClient("Certname goes here");
        }
        catch (AuthenticationException e)
        {
            Debug.WriteLine($"Exception: {e.Message}");
            if (e.InnerException != null)
            {
                Debug.WriteLine($"Inner exception: {e.InnerException.Message}");
            }
            Debug.WriteLine("Authentication failed - closing the connection.");
            return null;
        }

        return sslStream;
    }
  }

【讨论】:

    【解决方案2】:

    在下面找到一个通过 sslstream original source here 进行通信的客户端-服务器示例

    服务器

    static void Main(string[] args)
        {
            var _certificate = "certificate-string";
            TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000);
    
            server.Start();
    
            TcpClient client = server.AcceptTcpClient();
    
            SslStream stream = new SslStream(client.GetStream(), false, VerifyClientCertificate, null);
    
            X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(_certificate));
    
            stream.AuthenticateAsServer(certificate, false, 
                SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, false);
    
            if (stream.RemoteCertificate != null)
            {
                System.Console.WriteLine(stream.RemoteCertificate.Subject);
            }
            else
            {
                System.Console.WriteLine("No client certificate.");
            }
    
            StreamReader reader = new StreamReader(stream);
            StreamWriter writer = new StreamWriter(stream);
    
            bool clientClose = false;
            while (!System.Console.KeyAvailable)
            {
                System.Console.WriteLine("Waiting for data...");
                string line = reader.ReadLine();
                System.Console.WriteLine("Received: {0}", line);
                 
                if (line == "close")
                {
                    clientClose = true;
                    break;
                }
    
                writer.WriteLine(line);
                writer.Flush();
            }
    
            if (!clientClose) System.Console.ReadKey();
    
            stream.Close();
            server.Stop();
        }
    
        private static bool VerifyClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    

    客户

    static void Main(string[] args)
        {
            var _certificate = "certificate-string";
            TcpClient client = new TcpClient("127.0.0.1", 13000);
    
            SslStream stream = new SslStream(client.GetStream(), false, VerifyServerCertificate, null);
    
            X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(_certificate));
    
            stream.AuthenticateAsClient("127.0.0.1", new X509Certificate2Collection(certificate), 
                SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, false);
    
            StreamReader reader = new StreamReader(stream);
            StreamWriter writer = new StreamWriter(stream);
    
            while (true)
            {
                string line = System.Console.ReadLine();
                writer.WriteLine(line);
                writer.Flush();
                if (line == "close") break;
                line = reader.ReadLine();
                System.Console.WriteLine("Received: {0}", line);
            }
    
            stream.Close();
            client.Close();
        }
    
        private static bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多