2015-09-24 29 views
10

Sto tentando di effettuare chiamate HTTP a un server che richiede una connessione SSL bidirezionale (autenticazione client). Ho un file .p12 che contiene più di un certificato e una password. La richiesta viene serializzata utilizzando il buffer del protocollo.Autenticazione a due vie con HTTPClient

Il mio primo pensiero è stato quello di aggiungere il keystore alle proprietà ClientCertificate del WebRequestHandler utilizzato da HttpClient. Ho anche aggiunto il keystore alle mie autorità di certificazione radice attendibili sul mio computer.

Ricevo sempre un "impossibile creare un canale sicuro ssl/tls" quando viene eseguito PostAsync. C'è ovviamente qualcosa che faccio di sbagliato, ma qui sono un po 'in perdita.

Qualsiasi suggerimento sarebbe apprezzato.

public void SendRequest() 
    { 
     try 
     { 
      ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; 

      var handler = new WebRequestHandler(); 

      // Certificate is located in bin/debug folder 
      var certificate = new X509Certificate2Collection(); 
      certificate.Import("MY_KEYSTORE.p12", "PASSWORD", X509KeyStorageFlags.DefaultKeySet); 

      handler.ClientCertificates.AddRange(certificate); 
      handler.ServerCertificateValidationCallback = ValidateServerCertificate; 

      var client = new HttpClient(handler) 
      { 
       BaseAddress = new Uri("SERVER_URL") 
      }; 
      client.DefaultRequestHeaders.Add("Accept", "application/x-protobuf"); 
      client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-protobuf"); 
      client.Timeout = new TimeSpan(0, 5, 0); 

      // Serialize protocol buffer payload 
      byte[] protoRequest; 
      using (var ms = new MemoryStream()) 
      { 
       Serializer.Serialize(ms, MyPayloadObject()); 
       protoRequest = ms.ToArray(); 
      } 

      var result = await client.PostAsync("/resource", new ByteArrayContent(protoRequest)); 

      if (!result.IsSuccessStatusCode) 
      { 
       var stringContent = result.Content.ReadAsStringAsync().Result; 
       if (stringContent != null) 
       { 
        Console.WriteLine("Request Content: " + stringContent); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
      throw; 
     } 
    } 

     private 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; 
     } 

EDIT

non ho neanche rompere in ValidateServerCertificate. L'eccezione viene generata non appena viene chiamato PostAsync. Il protocollo è sicuramente TLS v1.

Il sistema operativo client è Windows 8.1. Server è codificato in Java (non sono sicuro su quale sistema operativo è in esecuzione su. Non ho accesso ad esso. E 'una scatola nera.)

Stacktrace

a System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult, TransportContext & contesto) a System.Net.Http.HttpClientHandler.GetRequestStreamCallback (IAsyncResult ar)

non c'è eccezione interna.

+0

Chi è l'emittente del certificato archiviato nel file .P12? il server si fida di tale emittente? –

+0

Viene emesso dal proprietario del server a cui sto tentando di connettermi (mi hanno dato il file). – Mathieu

+0

ValidateServerCertificate restituisce 'true' o' false'? Puoi inserire un breakpoint e controllare? Nel caso in cui restituisca false, qual è il valore di sslPolocyErrors? prova a restituire sempre 'true' dal metodo solo per verificare se questo risolve il problema? Forse la tua macchina locale non si fida dell'emittente del certificato del server? –

risposta

0

penso che sia quello che vi serve: Sample Asynchronous SslStream Client/Server Implementation

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


class Program 
{ 
    static void Main(string[] args) 
    { 
     SecureTcpServer server = null; 
     SecureTcpClient client = null; 

     try 
     { 
      int port = 8889; 

      RemoteCertificateValidationCallback certValidationCallback = null; 
      certValidationCallback = new RemoteCertificateValidationCallback(IgnoreCertificateErrorsCallback); 

      string certPath = System.Reflection.Assembly.GetEntryAssembly().Location; 
      certPath = Path.GetDirectoryName(certPath); 
      certPath = Path.Combine(certPath, "serverCert.cer"); 
      Console.WriteLine("Loading Server Cert From: " + certPath); 
      X509Certificate serverCert = X509Certificate.CreateFromCertFile(certPath); 

      server = new SecureTcpServer(port, serverCert, 
       new SecureConnectionResultsCallback(OnServerConnectionAvailable)); 

      server.StartListening(); 

      client = new SecureTcpClient(new SecureConnectionResultsCallback(OnClientConnectionAvailable), 
       certValidationCallback); 

      client.StartConnecting("localhost", new IPEndPoint(IPAddress.Loopback, port)); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex);    
     } 

     //sleep to avoid printing this text until after the callbacks have been invoked. 
     Thread.Sleep(4000); 
     Console.WriteLine("Press any key to continue..."); 
     Console.ReadKey(); 

     if (server != null) 
      server.Dispose(); 
     if (client != null) 
      client.Dispose(); 

    } 

    static void OnServerConnectionAvailable(object sender, SecureConnectionResults args) 
    { 
     if (args.AsyncException != null) 
     { 
      Console.WriteLine(args.AsyncException); 
      return; 
     } 

     SslStream stream = args.SecureStream; 

     Console.WriteLine("Server Connection secured: " + stream.IsAuthenticated); 



     StreamWriter writer = new StreamWriter(stream); 
     writer.AutoFlush = true; 

     writer.WriteLine("Hello from server!"); 

     StreamReader reader = new StreamReader(stream); 
     string line = reader.ReadLine(); 
     Console.WriteLine("Server Recieved: '{0}'", line == null ? "<NULL>" : line); 

     writer.Close(); 
     reader.Close(); 
     stream.Close(); 
    } 

    static void OnClientConnectionAvailable(object sender, SecureConnectionResults args) 
    { 
     if (args.AsyncException != null) 
     { 
      Console.WriteLine(args.AsyncException); 
      return; 
     } 
     SslStream stream = args.SecureStream; 

     Console.WriteLine("Client Connection secured: " + stream.IsAuthenticated); 

     StreamWriter writer = new StreamWriter(stream); 
     writer.AutoFlush = true; 

     writer.WriteLine("Hello from client!"); 

     StreamReader reader = new StreamReader(stream); 
     string line = reader.ReadLine(); 
     Console.WriteLine("Client Recieved: '{0}'", line == null ? "<NULL>" : line); 

     writer.Close(); 
     reader.Close(); 
     stream.Close(); 
    } 

    static bool IgnoreCertificateErrorsCallback(object sender, 
     X509Certificate certificate, 
     X509Chain chain, 
     SslPolicyErrors sslPolicyErrors) 
    { 
     if (sslPolicyErrors != SslPolicyErrors.None) 
     { 

      Console.WriteLine("IgnoreCertificateErrorsCallback: {0}", sslPolicyErrors); 
      //you should implement different logic here... 

      if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) 
      { 
       foreach (X509ChainStatus chainStatus in chain.ChainStatus) 
       { 
        Console.WriteLine("\t" + chainStatus.Status); 
       } 
      } 
     } 

     //returning true tells the SslStream object you don't care about any errors. 
     return true; 
    } 
} 
3

hai provato cambiare il vostro security protocol-Ssl3? In entrambi i casi, è necessario impostare la proprietà Expect su true. Risolverà il tuo errore. Inoltre è possibile esplorare this link per ottenere maggiori informazioni sul passaggio del certificato client per l'autenticazione.

public void SendRequest() 
{ 
    try 
    { 
     ServicePointManager.Expect100Continue = true; 
     ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; 

     var handler = new WebRequestHandler(); 
     ..... 
    } 
    .. 
} 
+0

Grazie per il tuo suggerimento. L'ho provato, ma ho ancora lo stesso errore di cui ho parlato nel mio post originale. – Mathieu

0

Hai provato il seguente codice?

ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true); 

Si prega di esso prima della riga di codice

handler.ClientCertificates.AddRange(certificate); 
1

stavo tentando di convalidare il Protocollo di sicurezza in uso quando mi sono imbattuto in questo post. Ho scoperto che ho generato un errore prima di ValidateServerCertificate quando stavo usando il protocollo di sicurezza errato. (IIS 7.x è impostato su SSL3.) Per coprire tutte le basi del protocollo da utilizzare, è possibile definire tutto. System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Ssl3;

0

Sto usando la stessa base di codice come voi, ma invece di utilizzare

certificate.Import("MY_KEYSTORE.p12", "PASSWORD", X509KeyStorageFlags.DefaultKeySet); 

Sto usando X509KeyStorageFlags.UserKeySet

anche io ho installato il certificato radice alla CurrentUser/CA e sta lavorando per me.