2015-06-07 70 views
1

我跟着this example創建我的測試證書。我用Certificate.cer服務器和Certificate.pfx客戶端:在TLS Web套接字服務器中使用SslStream的問題

makecert -r -pe -n "CN=Test Certificate" -sky exchange Certificate.cer -sv Key.pvk -eku 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2

"C:\Program Files (x86)\Windows Kits\8.1\bin\x64\pvk2pfx.exe" -pvk Key.pvk -spc Certificate.cer -pfx Certificate.pfx

我想創建一個web套接字服務器,並能夠正確通信的客戶端和服務器端驗證證書。這是我這我目前正在建設整個控制檯應用程序:當你運行它

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Net.Security; 
using System.Net.Sockets; 
using System.Net.WebSockets; 
using System.Security.Authentication; 
using System.Security.Cryptography.X509Certificates; 
using System.Text; 
using System.Threading.Tasks; 

namespace WebSockets 
{  
    class Program 
    { 
     static void Main(string[] args) 
     { 
      CreateWebSocketClient(CreateWebSocketServer(1337), 1338); 
      Console.WriteLine("Press any key to exit."); 
      Console.ReadKey(); 
     } 

     private static IPEndPoint CreateWebSocketServer(int port) 
     { 
      var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 
      IPEndPoint endpoint = new IPEndPoint(IPAddress.Loopback, port); 
      socket.Bind(endpoint); 
      socket.Listen(Int32.MaxValue); 
      socket.BeginAccept((result) => 
      { 
       var clientSocket = socket.EndAccept(result); 
       Console.WriteLine("{0}: Connected to the client at {1}.", DateTime.Now, clientSocket.RemoteEndPoint); 
       using (var stream = new SslStream(new NetworkStream(clientSocket), false, (sender, certificate, chain, sslPolicyErrors) => 
        { 
         return true; 
        }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => 
        { 
         return new X509Certificate2("Certificate.pfx"); 
        }, EncryptionPolicy.RequireEncryption)) 
       { 
        stream.AuthenticateAsServer(new X509Certificate2("Certificate.pfx"), true, SslProtocols.Tls12, true); 
        stream.Write("Hello".ToByteArray()); 
        Console.WriteLine("{0}: Read \"{1}\" from the client at {2}.", DateTime.Now, stream.ReadMessage(), clientSocket.RemoteEndPoint); 
       } 
      }, null); 
      Console.WriteLine("{0}: Web socket server started at {1}.", DateTime.Now, socket.LocalEndPoint); 
      return endpoint; 
     } 

     private static void CreateWebSocketClient(IPEndPoint remoteEndpoint, int port) 
     { 
      var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 
      IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Loopback, port); 
      socket.Bind(localEndpoint); 
      socket.BeginConnect(remoteEndpoint, (result) => 
      { 
       socket.EndConnect(result); 
       Console.WriteLine("{0}: Connected to the server at {1}.", DateTime.Now, remoteEndpoint); 
       using (var stream = new SslStream(new NetworkStream(socket), false, (sender, certificate, chain, sslPolicyErrors) => 
        { 
         return true; 
        }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => 
        { 
         return new X509Certificate2("Certificate.cer"); 
        }, EncryptionPolicy.RequireEncryption)) 
       { 
        stream.AuthenticateAsClient(remoteEndpoint.ToString(), new X509Certificate2Collection(new X509Certificate2[] { new X509Certificate2("Certificate.cer") }), SslProtocols.Tls12, true); 
        stream.Write("Hello".ToByteArray()); 
        Console.WriteLine("{0}: Read \"{1}\" from the server at {2}.", DateTime.Now, stream.ReadMessage(), remoteEndpoint); 
       } 
      }, null); 
     } 
    } 

    public static class StringExtensions 
    { 
     public static Byte[] ToByteArray(this String value) 
     { 
      Byte[] bytes = new Byte[value.Length * sizeof(Char)]; 
      Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length); 
      return bytes; 
     } 

     public static String FromByteArray(this Byte[] bytes) 
     { 
      Char[] characters = new Char[bytes.Length/sizeof(Char)]; 
      Buffer.BlockCopy(bytes, 0, characters, 0, bytes.Length); 
      return new String(characters).Trim(new Char[] { (Char)0 }); 
     } 

     public static int BufferSize = 0x400; 

     public static String ReadMessage(this SslStream stream) 
     { 
      var buffer = new Byte[BufferSize]; 
      stream.Read(buffer, 0, BufferSize); 
      return FromByteArray(buffer); 
     } 
    } 
} 

服務器和客戶端之間的通訊正常,但我不知道我應該如何實現的回調,特別是因爲sslPolicyErrors = RemoteCertificateNotAvailableRemoteCertificateValidationCallback是在客戶端調用RemoteCertificateValidationCallback時調用服務器端和sslPolicyErrors = RemoteCertificateNameMismatch | RemoteCertificateChainErrors。此外,certificatechain在服務器端爲空,但出現在客戶端的回調中。這是爲什麼?我的實現有什麼問題,以及如何使我的實現能夠正確驗證SSL證書?我試過在線搜索關於SslStream,但我還沒有看到一個完整的,基於X509的TLS服務器 - 客戶端實現,它實現了我需要的證書驗證類型。

回答

2

我有三個單獨的問題。我最初的做法是好的,但:這裏

  1. 我誤用的證書,如在客戶端解決我的問題RemoteCertificateNotAvailable使用.pfx證書。我不確定爲什麼.cer沒有工作。

  2. 我在調用AuthenticateAsClient指定了錯誤的主題名稱,如使用「測試證書」,爲第一個參數,而不是remoteEndpoint.ToString()解決了我的RemoteCertificateNameMismatch

  3. 儘管是自簽名的,爲了解決RemoteCertificateChainErrors錯誤,我必須將此證書添加到我當前用戶帳戶下的可信人員存儲庫中,以信任證書。

其他一些小的改進包括,和我得到的代碼,它接受多個客戶現在以及(因爲我有固定的上述一些錯誤),如下(請不要逐字因爲它需要複製此很多Pokemon exception handling在不同的地方,正確的清理邏輯,利用讀取調用讀取字節而不是修剪NUL,並引入一些Unicode字符(如EOT)來指定消息的結束,解析爲它,作爲以及處理由於我們的C#字符大小爲2個字節而不支持的奇數大小的緩衝區,處理奇數讀取等等;在它看到生產系統的光源並僅作爲示例之前,這需要很多細化或者是一個概念證明,如果你願意。):

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Net.Security; 
using System.Net.Sockets; 
using System.Net.WebSockets; 
using System.Security.Authentication; 
using System.Security.Cryptography.X509Certificates; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace WebSockets 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IPEndPoint server = CreateWebSocketServer(1337); 
      CreateWebSocketClient(server, 1338); 
      CreateWebSocketClient(server, 1339); 
      Console.WriteLine("Press any key to exit."); 
      Console.ReadKey(); 
     } 

     private static IPEndPoint CreateWebSocketServer(int port) 
     { 
      var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 
      IPEndPoint endpoint = new IPEndPoint(IPAddress.Loopback, port); 
      socket.Bind(endpoint); 
      socket.Listen(Int32.MaxValue); 
      ListenForClients(socket); 
      Console.WriteLine("{0}: Web socket server started at {1}.", DateTime.Now, socket.LocalEndPoint); 
      return endpoint; 
     } 

     private static void ListenForClients(Socket socket) 
     { 
      socket.BeginAccept((result) => 
      { 
       new Thread(() => 
       { 
        ListenForClients(socket); 
       }).Start(); 
       var clientSocket = socket.EndAccept(result); 
       Console.WriteLine("{0}: Connected to the client at {1}.", DateTime.Now, clientSocket.RemoteEndPoint); 
       using (var stream = new SslStream(new NetworkStream(clientSocket), false, (sender, certificate, chain, sslPolicyErrors) => 
       { 
        if (sslPolicyErrors == SslPolicyErrors.None) 
         return true; 
        return false; 
       }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => 
       { 
        return new X509Certificate2("Certificate.pfx"); 
       }, EncryptionPolicy.RequireEncryption)) 
       { 
        stream.AuthenticateAsServer(new X509Certificate2("Certificate.pfx"), true, SslProtocols.Tls12, true); 
        stream.Write("Hello".ToByteArray()); 
        Console.WriteLine("{0}: Read \"{1}\" from the client at {2}.", DateTime.Now, stream.ReadMessage(), clientSocket.RemoteEndPoint); 
       } 
      }, null); 
     } 

     private static void CreateWebSocketClient(IPEndPoint remoteEndpoint, int port) 
     { 
      var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 
      IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Loopback, port); 
      socket.Bind(localEndpoint); 
      socket.BeginConnect(remoteEndpoint, (result) => 
      { 
       socket.EndConnect(result); 
       Console.WriteLine("{0}: Client at {1} connected to the server at {2}.", DateTime.Now, localEndpoint, remoteEndpoint); 
       using (var stream = new SslStream(new NetworkStream(socket), false, (sender, certificate, chain, sslPolicyErrors) => 
       { 
        if (sslPolicyErrors == SslPolicyErrors.None) 
         return true; 
        return false; 
       }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => 
       { 
        return new X509Certificate2("Certificate.pfx"); 
       }, EncryptionPolicy.RequireEncryption)) 
       { 
        stream.AuthenticateAsClient("Test Certificate", new X509Certificate2Collection(new X509Certificate2[] { new X509Certificate2("Certificate.pfx") }), SslProtocols.Tls12, true); 
        stream.Write("Hello".ToByteArray()); 
        Console.WriteLine("{0}: Client at {1} read \"{2}\" from the server at {3}.", DateTime.Now, localEndpoint, stream.ReadMessage(), remoteEndpoint); 
       } 
      }, null); 
     } 
    } 

    public static class StringExtensions 
    { 
     public static Byte[] ToByteArray(this String value) 
     { 
      Byte[] bytes = new Byte[value.Length * sizeof(Char)]; 
      Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length); 
      return bytes; 
     } 

     public static String FromByteArray(this Byte[] bytes) 
     { 
      Char[] characters = new Char[bytes.Length/sizeof(Char)]; 
      Buffer.BlockCopy(bytes, 0, characters, 0, bytes.Length); 
      return new String(characters).Trim(new Char[] { (Char)0 }); 
     } 

     public static int BufferSize = 0x400; 

     public static String ReadMessage(this SslStream stream) 
     { 
      var buffer = new Byte[BufferSize]; 
      stream.Read(buffer, 0, BufferSize); 
      return FromByteArray(buffer); 
     } 
    } 
} 

我希望這可以幫助別人神祕性網絡插座,SSL數據流,X509證書,等等,在C#。快樂的編碼。 :)我可能會在我的博客上發佈最終版本。

+0

感謝分享代碼。您介意如何修改代碼以連接到wss://echo.websocket.org? –