C# SSLStream read, Stream read tcpclient

本文关键字:read tcpclient Stream SSLStream | 更新日期: 2023-09-27 18:07:59

我正在编写一个需要通过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();
    }

C# SSLStream read, Stream read tcpclient

我最终解决了这个问题,并想把答案贴出来给大家。证书是自签名的,因此"无效",但这在设备上很常见。我必须创建一个总是返回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;
    }
  }

下面是一个客户端-服务器通过sslstream通信的示例

服务器

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;
    }