利用C#实现SSLSocket加密通讯的方法详解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

利用C#实现SSLSocket加密通讯的方法详解

小y   2020-07-20 我要评论

本文着重给大家讲解了关于怎样利用C#实现SSLSocket加密通讯的相关资料,文中通过代码实例讲解的非常细致,对大家的工作和学习具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

SSL Socket通讯是对socket的扩展,增加Socket通讯的数据安全性,SSL认证分为单向和双向认证。单向认证只认证服务器端的合法性而不认证客户端的合法性。双向认证是同时认证服务端和客户端。下面我分别说说使用C#实现单向认证和双向认证的过程,并用代码实现。

一、 单向认证

第1步:准备一个数字证书,可以使用如下脚本生成

先进入到vs2005的命令行状态,即:

开始–>程序–>Microsoft Visual Studio 2005–>Visual Studio Tools–>Visual Studio 2005 命令提示

键入: makecert -r -pe -n “CN=TestServer” -ss Root -sky exchange

说明:上面的指令将在创建一个受信任的根证书,

第2步创建服务器端程序,代码如下:

using System;
using System.ServiceModel;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Text;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;

namespace ConsoleApp
{
public class Program
{
static X509Certificate serverCertificate = null;
 public static void RunServer()
  {
    TcpListener listener = new TcpListener(IPAddress.Parse("192.168.1.25"), 901);
    listener.Start();
    while (true)
    {
      try
      {
        Console.WriteLine("Waiting for a client to connect...");
        TcpClient client = listener.AcceptTcpClient();
        ProcessClient(client);
      }
      catch
      {
      }
    }
  }

  static void ProcessClient(TcpClient client)
  {
    SslStream sslStream = new SslStream(client.GetStream(), false);
    try
    {
      sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true);
      DisplaySecurityLevel(sslStream);
      DisplaySecurityServices(sslStream);
      DisplayCertificateInformation(sslStream);
      DisplayStreamProperties(sslStream);

      sslStream.ReadTimeout = 5000;
      sslStream.WriteTimeout = 5000;
      byte[] message = Encoding.UTF8.GetBytes("Hello from the server.");
      Console.WriteLine("Sending hello message.");
      sslStream.Write(message);
      Console.WriteLine("Waiting for client message...");
      while (true)
      {
        string messageData = ReadMessage(sslStream);
        Console.WriteLine("Received: {0}", messageData);
        if (messageData.ToUpper() == "EXIT")
          break;
      } 
    }
    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();
    }
  }

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

    return messageData.ToString();
  }

  static void DisplaySecurityLevel(SslStream stream)
  {
    Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
    Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
    Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
    Console.WriteLine("Protocol: {0}", stream.SslProtocol);
  }

  static void DisplaySecurityServices(SslStream stream)
  {
    Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
    Console.WriteLine("IsSigned: {0}", stream.IsSigned);
    Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
  }

  static void DisplayStreamProperties(SslStream stream)
  {
    Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
    Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
  }

  static void DisplayCertificateInformation(SslStream stream)
  {
    Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);

    X509Certificate localCertificate = stream.LocalCertificate;
    if (stream.LocalCertificate != null)
    {
      Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
      localCertificate.Subject,
        localCertificate.GetEffectiveDateString(),
        localCertificate.GetExpirationDateString());
    }
    else
    {
      Console.WriteLine("Local certificate is null.");
    }
    X509Certificate remoteCertificate = stream.RemoteCertificate;
    if (stream.RemoteCertificate != null)
    {
      Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
        remoteCertificate.Subject,
        remoteCertificate.GetEffectiveDateString(),
        remoteCertificate.GetExpirationDateString());
    }
    else
    {
      Console.WriteLine("Remote certificate is null.");
    }
  }

  private static void DisplayUsage()
  {
    Console.WriteLine("To start the server specify:");
    Console.WriteLine("serverSync certificateFile.cer");
  }

  public static void Main(string[] args)
  {
    try
    {
      X509Store store = new X509Store(StoreName.Root);
      store.Open(OpenFlags.ReadWrite);
      // 检索证书 
      X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectName, "TestServer", false); // vaildOnly = true时搜索无结果。
      if (certs.Count == 0) return;

      serverCertificate = certs[0];
      RunServer();
      store.Close(); // 关闭存储区。
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex.Message);
    }
    Console.ReadLine();
  }
}
}

第3步,创建客户端代码

namespace ConsoleAppClient
{
using System;
using System.Collections;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
namespace Examples.System.Net
{
  public class SslTcpClient
  {
    private static Hashtable certificateErrors = 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)
    {
      // Create a TCP/IP client socket.
      // machineName is the host running the server application.
      TcpClient client = new TcpClient(machineName, 901);
      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);
      try
      {
        sslStream.AuthenticateAsClient("TestServer");
      }
      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 "<EOF>".
      byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
      // 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);

      messsage = Encoding.UTF8.GetBytes("exit");
      sslStream.Write(messsage);
      sslStream.Flush();
      // Close the client connection.
      client.Close();
      Console.WriteLine("Client closed.");
    }

    static string ReadMessage(SslStream sslStream)
    {
      // Read the message sent by the server.
      // The end of the message is signaled using the
      // "<EOF>" marker.
      byte[] buffer = new byte[2048];
      StringBuilder messageData = new StringBuilder();
      int bytes = -1;
      do
      {
        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.
        if (messageData.ToString().IndexOf("<EOF>") != -1)
        {
          break;
        }
      } while (bytes != 0);

      return messageData.ToString();
    }

    private static void DisplayUsage()
    {
      Console.WriteLine("To start the client specify:");
      Console.WriteLine("clientSync machineName [serverName]");
      Environment.Exit(1);
    }

    public static void Main(string[] args)
    {
      string machineName = null;
      machineName = "192.168.1.25";
      try
      {
        RunClient(machineName);
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
      Console.ReadLine();
    }
  }
}
}

运行效果如下图:

导致通讯失败可能问题如下:

1)证书没有导入到受信任的根证书列表中;2)证书失效;3)客户端在使用AuthenticateAsClient注册时没有正确使用服务器端证书名称。

二、 双向认证

第1步:创建所需证书,服务器端所需证书同单向认证中的创建过程

先进入到vs2005的命令行状态,即:

开始–>程序–>Microsoft Visual Studio 2005–>Visual Studio Tools–>Visual Studio 2005 命令提示

键入:

makecert -r -pe -n “CN=TestClient” -ss Root -sky exchange

第2步:创建服务端程序

服务端的程序同单向认证的服务器端代码

第3步:创建客户端程序

namespace ConsoleAppClient
{
using System;
using System.Collections;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
namespace Examples.System.Net
{
  public class SslTcpClient
  {
    private static Hashtable certificateErrors = 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)
    {
      // Create a TCP/IP client socket.
      // machineName is the host running the server application.
      TcpClient client = new TcpClient(machineName, 901);
      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.

      X509Store store = new X509Store(StoreName.Root);
      store.Open(OpenFlags.ReadWrite);

      //// 检索证书 
      X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectName, "TestClient", false);        
      try
      {
        sslStream.AuthenticateAsClient("TestServer", 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();
        return;
      }
      // Encode a test message into a byte array.
      // Signal the end of the message using the "<EOF>".
      byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
      // 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);

      messsage = Encoding.UTF8.GetBytes("exit");
      sslStream.Write(messsage);
      sslStream.Flush();

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

    static string ReadMessage(SslStream sslStream)
    {
      // Read the message sent by the server.
      // The end of the message is signaled using the
      // "<EOF>" marker.
      byte[] buffer = new byte[2048];
      StringBuilder messageData = new StringBuilder();
      int bytes = -1;
      do
      {
        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.
        if (messageData.ToString().IndexOf("<EOF>") != -1)
        {
          break;
        }
      } while (bytes != 0);

      return messageData.ToString();
    }

    private static void DisplayUsage()
    {
      Console.WriteLine("To start the client specify:");
      Console.WriteLine("clientSync machineName [serverName]");
      Environment.Exit(1);
    }

    public static void Main(string[] args)
    {
      string machineName = null;
      machineName = "192.168.1.25";
      try
      {
        RunClient(machineName);
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
      Console.ReadLine();
    }
  }
}
}

总结

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们