【C#】Socket中的ssl通信

摘要:
有一个项目使用Socketssl通信。记录在这里。证书Socketssl需要使用证书来验证身份。对于调试,我们只需要使用测试证书。有一种工具可以轻松创建测试证书。下载地址为http://supersocket.codeplex.com/releases/view/59311首先,在输入CommonName、密码和保存路径后,我们可以获得证书服务器。p包含私钥

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

证书

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

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

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

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

客户端

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

【C#】Socket中的ssl通信第1张【C#】Socket中的ssl通信第2张
 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证书,且要把证书放在程序目录中,具体代码如下

【C#】Socket中的ssl通信第1张【C#】Socket中的ssl通信第4张
  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

免责声明:文章转载自《【C#】Socket中的ssl通信》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇11 libuboxIE低版本 canvas的支持下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

雷达信号的旁瓣对消和旁瓣匿影

1、雷达受干扰照射 对雷达来说,最难对付的干扰是有源干扰,对雷达的有源干扰不仅仅能从雷达的主瓣进入,还可以从雷达的旁瓣进入。通常情况下,雷达在搜索状态时,干扰信号是很难直接从雷达主瓣照射进来,都是通过旁瓣进入雷达接收机,如下图所示。                                                为了降低干扰信号进入雷达接收机...

几种flash存储芯片的用途和分类

1、IIC EEPROM------容量小,采用的是IIC通信协议;用于在掉电时,存系统配置参数,比如屏幕亮度等。常用芯片型号有 AT24C02、FM24C02、CAT24C02等,其常见的封装多为DIP8,SOP8,TSSOP8等; 2、SPI NorFlash------容量略大,采用的是SPI 通信协议;用于存放程序和数据。程序和数据可存放在同一芯片...

java进程间通信

Java进程间通信可以采用的办法: Socket/RMI/WEBService/WebServer, 这些都可以实现直接的数据交换 Database/File, 这些可以实现间接的数据交换   看你的业务是否要求实时, 如果不需要, 用数据库交换比较简单  除了Socket之外,当然首选的IPC可以使用RMI,或者CORBA也可以。其实JAVA的CORB...

linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁

Linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量、信号量和读写锁。 下面是思维导图:  一、互斥锁(mutex)   锁机制是同一时刻只允许一个线程执行一个关键部分的代码。 1 . 初始化锁 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t...

.NET插件技术-应用程序热升级

今天说一说.NET 中的插件技术,即 应用程序热升级。在很多情况下、我们希望用户对应用程序的升级是无感知的,并且尽可能不打断用户操作的。 虽然在Web 或者 WebAPI上,由于多点的存在可以逐个停用单点进行系统升级,而不影响整个服务。但是 客户端却不能这样做,毕竟用户一直在使用着。 那么有没有一种方式,可以在用户无感知的情况下(即、不停止进程的情况下)对...

关于socket通信bind()返回值错误:10049

前几天偶尔收到一个朋友的求救,要编写一个IPV6的socket编程 开始以为这个问题很容易,因为socket已经兼容IPV4和IPV6的,改下那几个接口就可以了 然后....被卡住了.... 修改了参数为IPV6的参数以后,在bind的时候始终包10049错误. 按照MSDN的说法,10049的意思表示找不到那个IP. 但是我服务器端bind监听的sock...