블로그 이미지
따시쿵

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Notice

2015. 3. 20. 18:00 C# with TCP/IP

프로그램 설명


SslStream 클래스를 이용한 통신 방법으로 구현 되었습니다.


보안이 없는 일반 tcp 서버/클라이언트와 비교해서 다른 점은 데이타를 주고 받기 전에 반드시 SSL 핸드세이크의 단계를 거친다는 점입니다.


아래는 msdn 에서 발취한 내용입니다.

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

SSL 핸드셰이크라고도 하는 인증 프로세스가 성공하면 서버와 클라이언트(선택적)의 ID가 설정되고 클라이언트와 서버는 SslStream을 사용하여 메시지를 주고받을 수 있습니다. 정보를 보내거나 받기 전에 클라이언트와 서버에서는 SslStream에 제공되는 보안 서비스 및 수준을 확인하여, 선택한 프로토콜, 알고리즘 및 강도가 무결성 및 기밀성 요구 사항을 충족하는지 확인해야 합니다.


작업을 하기 전에 인증서를 만드는 방법을 알고 개발에 사용할 인증서를 만들어야 합니다.


1.

Makecert.exe (Certificate Creation Tool) 사이트에서 다음의 예제와 같이 도스 창에서 명령어를 실행해서 작성합니다.


makecert -r -pe -n "CN=XYZ Company" -b 01/01/2005 -e 01/01/2010 -sky exchange -ss my


makecert 파일은 Windows 8.1 을 기준으로 아래의 경로에 있습니다.


 


2.

Visual Studio 를 이용해서 인증서를 테스트 할 수 있는 환경, 즉 사용할 수 있는 인증서로 만듭니다.








여기에 보여지는 소스는 msdn 사이트에 나오는 소스이며 원본 사이트는 아래와 같습니다.

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



실행 후




메시지 전송 후




프로그램 작성 방법


1. 서버 프로그램


using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;

    public sealed class SslTcpServer
    {
        static X509Certificate serverCertificate = null;

        public static void RunServer(string certificate, string password)
        {
            try
            {
                serverCertificate = new X509Certificate(certificate, password);
                
                TcpListener listener = new TcpListener(IPAddress.Any, 8080);
                listener.Start();

                while(true)
                {
                    Console.WriteLine("Waiting for a client to connect...");
                    Console.WriteLine();

                    TcpClient client = listener.AcceptTcpClient();
                    ProcessClient(client);
                }
            }
            catch(Exception ex)
            {
                Trace.WriteLine(string.Format("Error : {0}", ex.Message));
            }
        }

        static void ProcessClient(TcpClient client)
        {
            SslStream sslStream = new SslStream(client.GetStream(), false);

            try
            {
                sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true);
                
                // 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.Substring(0, messageData.IndexOf("$")));

                // Write a message to the client
                messageData = "[reply] " + messageData;
                byte[] message = Encoding.UTF8.GetBytes(messageData);
                sslStream.Write(message);
                Console.WriteLine("Sending hello message");
                Console.WriteLine();
            }
            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
            // "$" 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);

                // 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("$") != -1)
                {
                    break;
                }
            } while (bytes != 0);

            return messageData.ToString();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SslTcpServer.RunServer("XYZ_Company_ver2.pfx", "agrafood01");

            Console.WriteLine("Press the any key to continue...");
            Console.ReadLine();
        }
    }


2. 클라이언트 프로그램


using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
using System.Collections;

    public class SslTcpClient
    {
        private static Hashtable certtificateError = new Hashtable();

        // The following method is invoked by the RemoteCertificateValidationDelegate.
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

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

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }

        public static void RunClient(string machineName, string serverName, string inputMessage)
        {
            // Create a TCP/IP client socket.
            // machineName is the host running the server application.
            TcpClient client = new TcpClient(machineName, 8080);
            Console.WriteLine("Client connected.");
            // Create an SSL stream that will close the client's stream.
            SslStream sslStream = new SslStream(
                client.GetStream(),
                false,
                new RemoteCertificateValidationCallback(ValidateServerCertificate),
                null
                );
            // The server name must match the name on the server certificate.
            try
            {
                sslStream.AuthenticateAsClient(serverName);
            }
            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();
                return;
            }

            // Encode a test message into a byte array.
            // Signal the end of the message using the "$".
            byte[] messsage = Encoding.UTF8.GetBytes(inputMessage + "$");
            // Send hello message to the server. 
            sslStream.Write(messsage);
            sslStream.Flush();

            // Read message from the server.
            string serverMessage = ReadMessage(sslStream);
            Console.WriteLine("Server says: {0}", serverMessage.Substring(0, serverMessage.IndexOf("$")));


            // Close the client connection.
            client.Close();
            Console.WriteLine("Client closed.");
            Console.WriteLine();
        }

        private static string ReadMessage(SslStream sslStream)
        {
            // Read the message sent by the server.
            // The end of the message is signaled using the "$" marker
            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("$") != -1)
                {
                    break;
                }
            } while (bytes != 0);

            return messageData.ToString();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {            
            string inputMessage = string.Empty;

            do
            {
                Console.Write("Clients says: ");
                inputMessage = Console.ReadLine();
                SslTcpClient.RunClient("192.168.0.12", "XYZ Company", inputMessage);
            } while (inputMessage.ToLower() != "exit");
            
            Console.WriteLine("Press any key to continue...");
            Console.ReadLine();
        }
    }


posted by 따시쿵