2015-04-27 16 views
13

Tôi có một bộ nghe socket đa luồng. Nó lắng nghe trên một cổng. Nó nhận dữ liệu từ các trang web HTTP và gửi phản hồi theo yêu cầu của họ. Nó hoạt động tốt.Làm cách nào để áp dụng tệp chứng chỉ PFX cho trình nghe socket SslStream?

Bây giờ tôi muốn làm điều tương tự với trang web HTTPS. Vì vậy, tôi đã thay đổi nó để có thể đọc và viết thông qua SslStream thay vì socket.

Chủ sở hữu trang web đã cấp cho tôi tệp pfx và mật khẩu. Tôi đã đặt nó vào danh sách chứng chỉ đáng tin cậy của cửa hàng chứng nhận địa phương của tôi.

Tệp chứng nhận được truy xuất lại từ cửa hàng thành công. Nhưng sau khi gọi phương thức AuthenticateAsServer, socket xử lý của tôi dường như trống. Vì vậy, tôi không thể nhìn thấy bất kỳ dữ liệu nào trong nó bên trong phương thức ReceiveCallBack.

Tôi làm cách nào để áp dụng chứng chỉ pfx cho ổ cắm SslStream của mình?

Đây là mã của tôi:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Net.Security; 
using System.Threading; 
using System.Security.Cryptography.X509Certificates; 
using System.Text; 
using System.IO; 

namespace SslStreamTestApp 
{ 
    public class SslStreamSocketListener 
    { 
     private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false); 
     private readonly string pfxSerialNumber = "12345BLABLABLA"; 

     X509Certificate _cert = null; 

     private void GetCertificate() 
     { 
      X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser); 

      store.Open(OpenFlags.ReadOnly); 

      X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySerialNumber, pfxSerialNumber, true); 

      if (certificateCollection.Count == 1) 
      { 
       _cert = certificateCollection[0]; 
      } 
     } 

     private void StartListening() 
     { 
      GetCertificate(); 

      IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9002); 

      if (localEndPoint != null) 
      { 
       Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

       if (listener != null) 
       { 
        listener.Bind(localEndPoint); 
        listener.Listen(5); 

        Console.WriteLine("Socket listener is running..."); 

        listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 
       } 
      } 
     } 

     private void AcceptCallback(IAsyncResult ar) 
     { 
      _manualResetEvent.Set(); 

      Socket listener = (Socket)ar.AsyncState; 

      Socket handler = listener.EndAccept(ar); 

      SslStream stream = new SslStream(new NetworkStream(handler, true)); 
      stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Ssl3, true); 

      StateObject state = new StateObject(); 
      state.workStream = stream; 

      stream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state); 

      listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 
     } 

     private void ReceiveCallback(IAsyncResult result) 
     { 
      StateObject state = (StateObject)result.AsyncState; 
      SslStream stream = state.workStream; 

      // Check how many bytes received 
      int byteCount = stream.EndRead(result); 

      Decoder decoder = Encoding.UTF8.GetDecoder(); 

      char[] chars = new char[decoder.GetCharCount(state.buffer, 0, byteCount)]; 

      decoder.GetChars(state.buffer, 0, byteCount, chars, 0); 

      state.sb.Append(chars); 

      string check = state.sb.ToString(); 

      if (state.sb.ToString().IndexOf("<EOF>") == -1 && byteCount != 0) 
      { 
       // We didn't receive all data. Continue receiving... 
       stream.BeginRead(state.buffer, 0, state.buffer.Length, new AsyncCallback(ReceiveCallback), stream); 
      } 
      else 
      { 
       string[] lines = state.sb.ToString().Split('\n'); 

       // send test message to client 
       byte[] test = Encoding.UTF8.GetBytes("\"infoMessage\":\"test\""); 
       stream.BeginWrite(test, 0, test.Length, WriteAsyncCallback, stream); 
      } 
     } 

     private void WriteAsyncCallback(IAsyncResult ar) 
     { 
      SslStream sslStream = (SslStream)ar.AsyncState; 

      Console.WriteLine("Sending message to client..."); 

      try 
      { 
       sslStream.EndWrite(ar); 
      } 
      catch (IOException) 
      { 
       Console.WriteLine("Unable to complete send."); 
      } 
      finally 
      { 
       sslStream.Close(); 
      } 
     } 
    } 

    public class StateObject 
    { 
     public SslStream workStream = null; 
     public const int BufferSize = 1024; 
     public byte[] buffer = new byte[BufferSize]; 
     public StringBuilder sb = new StringBuilder(); 
    } 
} 

EDIT: Tôi nghĩ rằng tôi có một số vấn đề về SSL logic. Tệp PFX thuộc về trang web HTTPS. Nhưng các yêu cầu đến từ HTTPS đến trình nghe socket của tôi. Sau đó, trình nghe socket của tôi gửi phản hồi. Trong tình huống này, tôi là máy chủ hoặc máy khách? Tôi có sử dụng AuthenticateAsServer hoặc AuthenticateAsClient không?

Nó không ném lỗi nếu tôi gọi AuthenticateAsServer. Nó trở thành Authenticated không có vấn đề gì. Nhưng tôi không thể mang dữ liệu trong bộ xử lý socket đến phương thức ReceiveCallBack thông qua SslStream của StateObject của tôi.

+0

Có bất kỳ lý do nào mà bạn bắt đầu nhận được trên bộ xử lý, chứ không phải trên 'luồng SslStream'? Người gửi nhiều khả năng sẽ gửi dữ liệu được mã hóa. – Caramiriel

+0

Tôi không muốn thực hiện thay đổi lớn trong mã của mình vì nó hoạt động tốt. Vì vậy, tôi đang tìm kiếm nếu có một cách để sử dụng socket cho HTTPS. –

+0

Tôi không phải là chuyên gia về vận chuyển ở mức độ thấp, nhưng tôi đoán điều đó là không thể.Không thể mong đợi đọc một ổ cắm 'thô' và nhận được văn bản thuần trong khi nhận, nếu khách hàng đang sử dụng SSL. Bạn sẽ cần một cái gì đó để phân tích cú pháp/giải mã luồng SSL (đó là những gì 'SslStream' là dành cho). Bạn có thể điều chỉnh mã hiện tại để sử dụng 'Stream' /' NetworkStream'. Sau đó, 'SslStream' nên khá dễ dàng. – Caramiriel

Trả lời

1

Bạn có thể tải các tập tin PFX trực tiếp với:

byte[] pfxData = File.ReadAllBytes("myfile.pfx"); 

cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); 

Nếu bạn đang tải nó từ cửa hàng cert importand của mình rằng phím là có thể xuất khẩu cho ứng dụng.

Thông tin: SSL phiên bản 3.0 không còn an toàn nữa. Có thể đó là lý do tại sao bạn không thể có kết nối. Chrome và Firefox đã tắt hỗ trợ.

Mẹo: Viết một máy khách SSL đơn giản để gửi một số byte "Hello World".

Tôi đã thử nghiệm mã của bạn với chứng chỉ tự ký theo định dạng pfx trên ứng dụng khách C# và trình duyệt chrome. cả hai khách hàng đều có thể kết nối và gửi/nhận dữ liệu.

điều duy nhất tôi đã thay đổi tôi đặt SSLProtocol để TLS

stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true); 

Thông tin: HTTPS là giao thức HTTP qua kết nối tcp/tls. Bạn có kết nối tcp/tls thô không có giao thức http.

CHỈ DẪN: Đừng quên mở Cổng Tường lửa 9002!

+0

Tôi làm cách nào để có thể xuất được cho ứng dụng? Nó được lưu trữ trong các nhà xuất bản đáng tin cậy của cửa hàng cert. Tôi đã mở nó thông qua cửa hàng cert cũng tôi thêm nó vào giải pháp và cố gắng mở nó trực tiếp từ đó nhưng tôi không thể đạt được. Tôi đã thử Tls thay vì SSL3 nhưng không giúp đỡ. –

+0

Bạn có thể kiểm tra khóa riêng với: nếu (! Cert.HasPrivateKey) ném ngoại lệ mới ("khóa riêng không được tải"); Tôi sẽ kiểm tra mã của bạn sau vài giờ nữa. –

Các vấn đề liên quan