2015-04-27 1 views
13

J'ai un écouteur de socket multi-threaded. Il écoute sur un port. Il reçoit des données des sites Web HTTP et envoie une réponse en fonction de leur demande. Cela fonctionne bien. Maintenant, je veux faire la même chose avec un site Web HTTPS. Donc je l'ai changé pour pouvoir lire et écrire via SslStream au lieu de socket.Comment appliquer le fichier de certificat PFX à l'écouteur de socket SslStream?

Le propriétaire du site Web m'a donné un fichier pfx et un mot de passe. Je l'ai placé dans la liste des certificats de confiance de mon magasin de certification local.

Le fichier de certification est récupéré du magasin avec succès. Mais après avoir appelé la méthode AuthenticateAsServer, mon socket de gestionnaire semble être vide. Je ne vois donc pas de données dans la méthode ReceiveCallBack. Comment appliquer un certificat pfx à mon socket SslStream?

Voici mon code:

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: je pense avoir un problème sur la logique SSL. Le fichier PFX appartient au site Web HTTPS. Mais les demandes proviennent de HTTPS à mon écouteur de socket. Ensuite, mon écouteur de socket envoie une réponse. Dans cette situation, suis-je serveur ou client? Est-ce que j'utiliserai AuthenticateAsServer ou AuthenticateAsClient?

Il ne lance pas d'erreur si j'appelle AuthenticateAsServer. Il devient authentifié sans problème. Mais je ne peux pas porter les données dans le socket du gestionnaire à la méthode ReceiveCallBack via le SslStream de mon StateObject.

+0

Y a-t-il une raison pour laquelle vous commencez à recevoir sur le gestionnaire lui-même, et non sur 'SslStream stream'? L'expéditeur envoie très probablement des données cryptées de toute façon. – Caramiriel

+0

Je ne veux pas faire de gros changements dans mon code parce que ça fonctionne bien. Donc, je cherche s'il y a un moyen d'utiliser socket pour HTTPS. –

+0

Je ne suis pas un expert en transport à basse altitude, mais je suppose que c'est impossible.Vous ne pouvez pas vous attendre à lire un socket «brut» et obtenir du texte brut lors de la réception, si le client utilise SSL. Vous aurez besoin de quelque chose pour analyser/décoder le flux SSL (c'est à quoi sert 'SslStream'). Vous pouvez adapter le code existant pour utiliser 'Stream' /' NetworkStream'. Alors 'SslStream' devrait être assez facile. – Caramiriel

Répondre

1

Vous pouvez charger le fichier PFX direct avec:

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

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

Si vous le chargez du magasin cert son importand qui la clé est exportable pour l'application.

Info: SSL version 3.0 n'est plus sécurisé. Peut-être que c'est pourquoi vous ne pouvez pas obtenir une connexion. Chrome et Firefox ont désactivé le support. Conseil: Écrivez un client SSL simple pour envoyer des octets "Hello World".

J'ai testé votre code avec un certificat auto-signé au format pfx sur un client C# et le navigateur chrome. les deux clients pouvaient se connecter et envoyer/recevoir des données.

la seule chose que j'ai changé je mis le SSLProtocol à TLS

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

Info: HTTPS est le protocole HTTP via une connexion tcp/tls. Vous avez une connexion tcp/tls brute sans le protocole http. ASTUCE: N'oubliez pas d'ouvrir le port de pare-feu 9002!

+0

Comment puis-je le rendre exportable pour l'application? Il est stocké dans les éditeurs de confiance du magasin de certificats. Je l'ai ouvert via le magasin de cert aussi je l'ai ajouté à la solution et essayé de l'ouvrir directement à partir de là, mais je ne pouvais pas réaliser. J'ai essayé Tls au lieu de SSL3 mais n'ai pas aidé. –

+0

Vous pouvez tester la clé privée avec: if (! Cert.HasPrivateKey) throw new Exception ("clé privée non chargée."); Je vais tester votre code dans quelques heures. –