• 【C#】Socket中的ssl通信


    引言 
       有个项目中用到了Socket ssl通信,在此记录一下.

    证书

       Socket ssl需要用到证书用来校验身份,而作为调试,我们只需用测试证书即可.

       有个工具可以很方便地制作测试证书,下载地址为http://supersocket.codeplex.com/releases/view/59311

       首先, 输入Common Name,密码和保存路径后,我们可以得到包含私钥的证书server.pfx.

       然后,安装证书到电脑中,在IE选项中导出一份证书作为client.cer.

    客户端

       使用客户端的电脑需要安装client.cer到<受信任的根证书颁发机构>,且要把证书放在程序目录中,具体代码如下

     class Program
        {
            private static SslStream _sslStream;
    
            static void Main(string[] args)
            {
    
                try
                {
                    TcpClient client = new TcpClient("127.0.0.1", 6000);
                    Console.WriteLine("Client connected.");
                    _sslStream = new SslStream(
                       client.GetStream(),
                       false,
                       new RemoteCertificateValidationCallback(ValidateServerCertificate),
                       null
                       );
    
                 
                    X509CertificateCollection certs = new X509CertificateCollection();
                    X509Certificate cert = X509Certificate.CreateFromCertFile(System.Environment.CurrentDirectory + @"" + "client.cer");
                    certs.Add(cert);
                    //验证证书
                    try
                    {
                        _sslStream.AuthenticateAsClient("test", certs, SslProtocols.Tls, false);
                    }
                    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.");
                        client.Close();
                        Console.ReadLine();
                        return;
                    }
    
                    //开始读取消息
                    Task.Factory.StartNew(() =>
                    {
                        ReadMessage(_sslStream);
                    });
    
                    Console.WriteLine("按Q退出程序");
                    string message = "";
                    message = Console.ReadLine() + "<EOF>";
                    while (message != "Q")
                    {
                        byte[] bytes = Encoding.UTF8.GetBytes(message);
                        _sslStream.Write(bytes);
                        _sslStream.Flush();
                        Console.WriteLine("send:" + message);
                        message = Console.ReadLine() + "<EOF>";
                    }
    
                    client.Close();
                }
                catch (Exception ex)
                {
                    
                    Console.WriteLine(ex);
                    Console.ReadLine();
                }
            }
    
            public static void ReadMessage(SslStream sslStream)
            {
                byte[] buffer = new byte[2048];
                StringBuilder messageData = new StringBuilder();
                int bytes = -1;
                do
                {
                    bytes = sslStream.Read(buffer, 0, buffer.Length);
                    Decoder decoder = Encoding.UTF8.GetDecoder();
                    char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                    decoder.GetChars(buffer, 0, bytes, chars, 0);
                    messageData.Append(chars);
                    if (messageData.ToString().IndexOf("<EOF>", StringComparison.Ordinal) != -1)
                    {
                        break;
                    }
                } while (bytes != 0);
    
                string message = messageData.ToString().Replace("<EOF>", "");
                Console.WriteLine("recevied:" + message);
                ReadMessage(sslStream);
            }
    
            private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
            {
                if (sslpolicyerrors == SslPolicyErrors.None)
                    return true;
                Console.WriteLine("Certificate error: {0}", sslpolicyerrors);
                return false;
            }
        }
    View Code

    服务端

       服务端电脑要安装server.pfx证书,且要把证书放在程序目录中,具体代码如下

      class Program
        {
            static void Main(string[] args)
            {
                TcpListener listener = new TcpListener(IPAddress.Any, 6000);
                listener.Start();
    
                Console.WriteLine("Waiting for a client to connect...");
                TcpClient client = listener.AcceptTcpClient();
    
                _sslStream = new SslStream(client.GetStream(), true);
    
                try
                {
                    serverCertificate = new X509Certificate(Environment.CurrentDirectory + @"" + "server.pfx", "1");
                    _sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true);
                }
                catch (Exception ex)
                {
                     Console.WriteLine(ex);
                     Console.ReadLine();
                     return;
                }
           
                while (true)
                {
                    string receivedMessage = ReadMessage(_sslStream);
                    Console.WriteLine("received:" + receivedMessage);
                    byte[] message = Encoding.UTF8.GetBytes("Success.<EOF>");
                    _sslStream.Write(message);
                    _sslStream.Flush();
                }
            }
    
            static X509Certificate serverCertificate = null;
            private static SslStream _sslStream;
    
    
    
            static string ReadMessage(SslStream sslStream)
            {
                byte[] buffer = new byte[2048];
                StringBuilder messageData = new StringBuilder();
                int bytes = -1;
                do
                {
                    bytes = sslStream.Read(buffer, 0, buffer.Length);
                    Decoder decoder = Encoding.UTF8.GetDecoder();
                    char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                    decoder.GetChars(buffer, 0, bytes, chars, 0);
                    messageData.Append(chars);
                    if (messageData.ToString().IndexOf("<EOF>") != -1)
                    {
                        break;
                    }
                } while (bytes != 0);
    
                return messageData.ToString();
            }
    
            static void ProcessClient(TcpClient client)
            {
    
                SslStream sslStream = new SslStream(
                    client.GetStream(), true);
    
                try
                {
                    sslStream.AuthenticateAsServer(serverCertificate, true, SslProtocols.Tls, true);
    
                    Console.WriteLine("Waiting for client message...");
                    string messageData = ReadMessage(sslStream);
                    Console.WriteLine("Received: {0}", messageData);
    
                    byte[] message = Encoding.UTF8.GetBytes("已收到信息.<EOF>");
                    sslStream.Write(message);
                    sslStream.Flush();
                }
                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
                {
                    sslStream.Close();
                    client.Close();
                }
            }
    
        }
    View Code

    注意事项

      1.服务端验证方法AuthenticateAsServer的参数clientCertificateRequired如果为true,那在客户端也要安装server.pfx.

      2.客户端验证方法AuthenticateAsClient的参数targetHost对应证书中Common Name,也就是受颁发者.

    参考资料

       https://msdn.microsoft.com/zh-cn/library/system.net.security.sslstream(v=vs.110).aspx

  • 相关阅读:
    Web.py Cookbook 简体中文版
    'IContextMenu' : no GUID has been associated with this object问题的解决
    switchflow/switchflow/asiohttp at master · baus/switchflow · GitHub
    面试体验:Facebook 篇
    Nodejs抓取非utf8字符编码的页面
    小体会
    做socket保持长连接的一些心得
    【资料整理】Socket编程之非阻塞connect(二)
    Less copies in Python with the buffer protocol and memoryviews
    » 文档与笔记利器 reStructuredText 和 Sphinx Wow! Ubuntu
  • 原文地址:https://www.cnblogs.com/caizl/p/5836943.html
Copyright © 2020-2023  润新知