2015-12-14 62 views
17

Abbiamo un'applicazione Web in esecuzione sul server e pubblica richieste http tramite XDomainRequest (a causa di IE9).La certificazione SSL funziona con localhost ma non con il nome del computer o IP

Ci sono molti computer client che hanno un'applicazione di console in ascolto su una porta tramite listener di socket. I client di applicazioni web aperto con le loro browser IE9 e quando fanno clic su un link, la pagina web invia le richieste del genere:

"https://localhost:portNumber/applicationName/doSomething" "https://computerName:portNumber/applicationName/doSomething" "https://ipAddress:portNumber/applicationName/doSomething"

La seconda e terza richieste sono fatto per console applicazioni dei computer degli altri utenti.

Il problema è che se le richieste arrivano con localhost, l'applicazione della console non ha problemi a leggere i dati in arrivo e ad inviare la risposta. Ma se la richiesta viene fornita con il nome del computer o l'indirizzo IP, il browser mostra l'avviso di certificazione e vuole che l'utente faccia clic sul link "Continua su questo sito web (non consigliato)".

Abbiamo pensato di creare tre certificati diversi tramite codice. Ma anche usando sslstream con tre di questi è possibile, non possiamo decidere di selezionare la vera certificazione perché prima facciamo l'autenticazione e poi riceviamo i dati. Quindi, quando intercettiamo la richiesta in arrivo, l'autenticazione deve essere già eseguita.

Un altro modo è forzare il listener di socket o sslstream a comportarsi tutte queste tre richieste come se fossero localhost. Quindi per ognuna l'autenticazione sarà fatta come localhost. Ma non sono riuscito a trovare un modo per farlo.

Ecco il codice. Fornisco il codice perché forse c'è un uso errato di SslStream.

using System; 
using System.Net.Sockets; 
using System.Net; 
using System.Configuration; 
using System.Security.Cryptography.X509Certificates; 
using System.Windows.Forms; 
using System.IO; 
using System.Net.Security; 
using System.Security.Authentication; 
using System.Threading; 
using System.Text; 

namespace StackOverFlowProject 
{ 
    class StackOverFlowSample 
    { 
     private static ManualResetEvent _manualResetEvent = new ManualResetEvent(false); 
     private static X509Certificate _cert = null; 

     static void Main(string[] args) 
     { 
      StackOverFlowSample stackOverFlowSample = new StackOverFlowSample(); 
      stackOverFlowSample.StartListening(); 
     } 

     private void StartListening() 
     { 
      GetCertificate(); 

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

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

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

        Console.WriteLine("Socket listener is running. Waiting for requests..."); 

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

     private static void GetCertificate() 
     { 
      byte[] pfxData = File.ReadAllBytes(Application.StartupPath + @"\" + "localhost.pfx"); 

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


     private void AcceptCallback(IAsyncResult result) 
     { 
      Socket listener = null; 
      Socket handler = null; 
      StateObject state = null; 
      SslStream sslStream = null; 

      _manualResetEvent.Set(); 

      listener = (Socket)result.AsyncState; 

      handler = listener.EndAccept(result); 

      state = new StateObject(); 

      if (handler.RemoteEndPoint != null) 
      { 
       state.clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString(); 
      } 

      sslStream = new SslStream(new NetworkStream(handler, true)); 
      sslStream.AuthenticateAsServer(_cert, false, SslProtocols.Tls, true); 

      sslStream.ReadTimeout = 100000; 
      sslStream.WriteTimeout = 100000; 

      state.workStream = sslStream; 

      if (state.workStream.IsAuthenticated) 
      { 
       state.workStream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state); 
      } 

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

     private void ReceiveCallback(IAsyncResult result) 
     { 
      StateObject stateObject = null; 
      SslStream sslStreamReader = null; 

      byte[] byteData = null; 

      stateObject = (StateObject)result.AsyncState; 
      sslStreamReader = stateObject.workStream; 

      int byteCount = sslStreamReader.EndRead(result); 

      Decoder decoder = Encoding.UTF8.GetDecoder(); 
      char[] chars = new char[decoder.GetCharCount(stateObject.buffer, 0, byteCount)]; 
      decoder.GetChars(stateObject.buffer, 0, byteCount, chars, 0); 
      stateObject.sb.Append(chars); 

      if (byteCount > 0) 
      { 
       stateObject.totalReceivedBytes += byteCount; 

       string[] lines = stateObject.sb.ToString().Split('\n'); 

       if (lines[lines.Length - 1] != "<EOF>") 
       { 
        // We didn't receive all data. Continue reading... 
        sslStreamReader.BeginRead(stateObject.buffer, 0, stateObject.buffer.Length, new AsyncCallback(ReceiveCallback), stateObject); 
       } 
       else 
       { 
        Console.WriteLine("We received all data. Sending response..."); 

        byteData = Encoding.UTF8.GetBytes("Hello! I received your request!"); 

        string httpHeaders = "HTTP/1.1" + "\r\n" 
            + "Cache-Control: no-cache" + "\r\n" 
            + "Access-Control-Allow-Origin: *" + "\r\n" 
            + "\r\n"; 

        byte[] byteHttpHeaders = Encoding.UTF8.GetBytes(httpHeaders); 

        byte[] concat = new byte[byteHttpHeaders.Length + byteData.Length]; 
        Buffer.BlockCopy(byteHttpHeaders, 0, concat, 0, byteHttpHeaders.Length); 
        Buffer.BlockCopy(byteData, 0, concat, byteHttpHeaders.Length, byteData.Length); 

        stateObject.sslStreamReader = sslStreamReader; 

        sslStreamReader.BeginWrite(concat, 0, concat.Length, new AsyncCallback(SendCallback), stateObject); 
       } 
      } 
     } 

     private void SendCallback(IAsyncResult ar) 
     { 
      SslStream sslStreamSender = null; 

      StateObject stateObject = (StateObject)ar.AsyncState; 

      sslStreamSender = stateObject.sslStreamReader; 
      sslStreamSender.EndWrite(ar); 

      Console.WriteLine(stateObject.totalReceivedBytes.ToString() + " bytes sent to " + stateObject.clientIP + " address"); 

      sslStreamSender.Close(); 
      sslStreamSender.Dispose(); 
     } 

    } 

    public class StateObject 
    { 
     public SslStream workStream = null; 

     public SslStream sslStreamReader = null; 

     public const int BufferSize = 1024; 
     public byte[] buffer = new byte[BufferSize]; 
     public StringBuilder sb = new StringBuilder(); 

     public string clientIP = ""; 

     public int totalReceivedBytes = 0; 
    } 
} 
+1

Questo perché il CN del certificato è localhost. Dovrai utilizzare un certificato appropriato che corrisponda al nome host della richiesta in modo che la convalida funzioni. –

+0

Diciamo che il nome del computer che esegue il sito web è server1. Il certificato avrà CN = server1 informazioni? –

+0

Ma quando apro il file PFX con certmgr vedo che CN è impostato su qualcosa di diverso da localhost. –

risposta

1

L'avviso del certificato che si sta verificando è in realtà un errore di mancata corrispondenza del nome, che indica che il nome comune (nome dominio) nel certificato SSL non corrisponde all'URL/indirizzo utilizzato per accedere al sito Web/server.

https://www.sslshopper.com/ssl-certificate-name-mismatch-error.html

Nel vostro scenario di utilizzo, si può decidere di passare lontano da indirizzi localhost e IP a favore di un semplice modello di dominio che sfrutta il nome del computer. (per esempio.)

Quindi, è possibile ottenere un certificato con caratteri jolly (ad esempio *.someDomain.com) che potrebbe essere utilizzato per autenticare le comunicazioni tra processi.

https://www.sslshopper.com/best-ssl-wildcard-certificate.html

5

Il responsabile della sicurezza ha ragione. Il modo in cui stai cercando di farlo non funzionerà con SSL.

Se si dispone di un certificato, è impostato per autenticare un CN. Quindi, per un esempio semplicistico. Google ha un certificato. Autentica https: //*.google.com. Ciò significa che qualsiasi richiesta a google.com risulta avere un certificato valido. E il tuo browser è felice.

Ora apri un prompt dei comandi, ping google.com. Prendi l'indirizzo IP (nel mio caso è uscito come 216.58.210.14). Immettere https://216.58.210.14. Il tuo browser si lamenta del fatto che il sito non è sicuro ecc. Il motivo è che il server potrebbe essere lo stesso che ha servito la tua richiesta precedente, ma il modo in cui ci stai arrivando non è valido in base al certificato, in quanto il CN non è google. com, ma un indirizzo IP.

Quindi se si dispone di un servizio a cui è necessario connettersi (ad esempio) 127.0.0.1, 10.92.1.4 E myserver.com, è necessario un certificato valido per ciascun caso.

+0

Vuol dire che abbiamo bisogno di tre diversi file di certificazione o possiamo creare un file di certificazione che contiene queste tre opzioni? –

+0

La tua ultima frase mi ha confuso. Il resto è OK. –

+0

Un certificato può, per definizione, funzionare solo per un dominio. Quindi ne avresti bisogno uno per ciascuno. Potresti riuscire a ovviare a questo aggiungendo voci al tuo file hosts che associa un IP a un particolare dominio, ma dovresti farlo su ogni computer degli utenti. –

1

io non sono sicuro che i tuoi "https://computerName:portNumber/applicationName/doSomething" mezzi. Quindi non sei sicuro se stai usando il certificato giusto o se i percorsi/connessione a cui stai accedendo o usando nel codice siano corretti. https://technet.microsoft.com/en-us/library/dd891009.aspx

Può accedere a qualsiasi protocollo non limitato a TCP. https://technet.microsoft.com/en-us/library/cc784450(v=ws.10).aspx

  • Wildcard SSL Certificates consentono agli amministratori di garantire un numero illimitato di sottodomini sullo stesso dominio con un singolo certificato SSL. Un certificato SSL WildCard viene emesso su * .yourdomain.com che offre vantaggi in termini di risparmi derivanti dall'acquisto di numerosi certificati SSL per ogni sottodominio e richiede solo l'uso di un solo indirizzo IP.
  • I certificati SSL tipici proteggono solo un nome di dominio completo. I certificati di Unified Communications consentono di assegnare più nomi host, noti come Nomi alternativi o SAN soggetti, in un certificato. Per una flessibilità totale, un certificato SSL multi-dominio consente l'uso del campo Nome alternativo soggetto (SAN) in modo da poter proteggere fino a 100 diversi nomi di domini, sottodomini o indirizzi IP pubblici utilizzando un singolo certificato SSL e richiedendo solo uno Indirizzo IP.

** Il valore deve corrispondere esattamente al nome host o al nome di dominio a cui si accede. Altrimenti continuerai a ricevere errori nel certificato del browser dopo che il certificato è stato importato e considerato affidabile. Ad es .: se in precedenza si utilizzava http://101.10.10.1:9000/ per accedere al sito e il nome comune del nuovo certificato è "mydomain", il collegamento deve essere aggiornato a http://mydomain:9000/; se hai creato un certificato per mydomain:9000. Secondo, usa il nome host o CNAME con cui indirizzerai il server. Questo è molto importante. Se il vero nome host del tuo server web è mysubdomain.mydomain.com, le persone useranno www.mydomain.com per indirizzare la casella, quindi utilizzare quest'ultimo nome per rispondere alla domanda "Nome comune" (CNAME).

** Controllare se si è in grado di connettersi utilizzando (i comandi SSL uso specifico per risolvere i problemi, cito OpenSSL) la openssl s_time -connect remote.host:443 o openssl s_time -connect remote.host:443 -www /test.html -new ** Se non si dispone di un server Web SSL-enabled disponibile per la vostra usa, puoi emularne uno usando l'opzione s_server.

# on one host, set up the server (using default port 4433) openssl s_server -cert mycerti.pem -www # on second host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www/-new -ssl3

o

# on second host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www/-new -ssl3 ** Controllare se CERT è in uso? $ openssl s_client -connect [ip_or_dns_name]:[port] $ man s_client

0

soluzione che propongo è avere assunto:

  • sito è ospitato su IIS

  • è possibile accedere all'applicazione dal nome della macchina attraverso la rete senza alcun problema.

  • non è necessario accedere a webapp su internet (che avrà bisogno di certificato da autorità certificata).

Prova:

  1. creare auto certificato firmato da IIS come:

    i. Aprire IIS, selezionare Certificati server come: enter image description here

    ii. Creare un certificato autofirmato come: enter image description here

    iii. configurare il sito Web per utilizzare tale certificato come:

    • selezionare il sito Web, fare clic su binding.

    • Fare clic su Modifica binding per https

enter image description here

iv. Modifica sito di legame per usare il certificato che avete creato come:

enter image description here

Ora è possibile accedere a voi l'applicazione con il nome della macchina.

Stesso processo che è possibile ripetere per i certificati di dominio per Internet ma che richiede un certificato registrato/fornito da un'autorità certificata.

Si prega di notare che ora l'uso può accedere all'applicazione con Nome computer su entrambi i locali & sulla rete. Poiché il certificato viene emesso per nome host/co-dominio singolo, utilizzare il nome del computer invece di localhost sul computer locale.