2015-09-24 4 views
10

Ich versuche, HTTP-Aufrufe an einen Server vorzunehmen, der eine bidirektionale SSL-Verbindung (Client-Authentifizierung) erfordert. Ich habe eine .p12-Datei, die mehr als ein Zertifikat und ein Passwort enthält. Die Anfrage wird unter Verwendung des Protokollpuffers serialisiert.Zwei-Wege-Authentifizierung mit HTTPClient

Mein erster Gedanke war, den Keystore zu den ClientCertificate-Eigenschaften des WebRequestHandler, der von dem HttpClient verwendet wird, hinzuzufügen. Ich habe den Schlüsselspeicher auch meinen vertrauenswürdigen Stammzertifizierungsstellen auf meinem Computer hinzugefügt.

Ich bekomme immer eine "konnte nicht SSL/Tls sicheren Kanal erstellen", wenn der PostAsync ausgeführt wird. Es gibt offensichtlich etwas, das ich falsch mache, aber ich bin hier ein wenig verloren.

Alle Hinweise würden geschätzt.

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

breche ich nicht einmal in ValidateServerCertificate. Sobald der PostAsync aufgerufen wird, wird eine Ausnahme ausgelöst. Protokoll ist definitiv TLS v1.

Client-Betriebssystem ist Windows 8.1. Server ist codiert in Java (nicht sicher, auf welchem ​​Betriebssystem er läuft auf. Ich habe keinen Zugriff auf sie. Es ist eine Blackbox.)

Stacktrace

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

Es gibt keine innere Ausnahme.

+0

Wer ist der Aussteller des Zertifikats, das in der .P12-Datei gespeichert ist? Glaubt der Server einem solchen Aussteller? –

+0

Es wird vom Besitzer des Servers ausgegeben, mit dem ich eine Verbindung herstellen möchte (sie gaben mir die Datei). – Mathieu

+0

Gibt ValidateServerCertificate 'true' oder' false' zurück? Kannst du einen Haltepunkt setzen und überprüfen? Falls es falsch zurückgibt, was ist der Wert von sslPolocyErrors? Versuchen Sie immer 'true' von der Methode zurückzugeben, nur um zu testen, ob dies das Problem löst? Vielleicht vertraut Ihr lokaler Computer dem Aussteller des Serverzertifikats nicht? –

Antwort

0

Ich denke, es ist das, was Sie brauchen: 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

Versuchen Sie Ihre security protocol-Ssl3 zu ändern? In beiden Fällen müssten Sie die Eigenschaft Expect auf true setzen. Es wird Ihren Fehler beheben. Weiter können Sie this link erkunden, um mehr Wissen über die Weitergabe von Client-Zertifikaten für die Authentifizierung zu erhalten.

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

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

Danke für Ihren Vorschlag. Ich habe es versucht, aber ich bekomme immer noch den gleichen Fehler wie in meinem ursprünglichen Beitrag erwähnt. – Mathieu

0

Haben Sie folgenden Code ausprobiert?

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

Bitte es vor Ihrer Codezeile

handler.ClientCertificates.AddRange(certificate); 
1

Ich war versucht, das Sicherheitsprotokoll zur Validierung verwendet wird, wenn ich auf diesen Beitrag kam. Ich habe festgestellt, dass ich vor ValidateServerCertificate einen Fehler ausgegeben habe, als ich das falsche Security Protocol verwendet habe. (IIS 7.x ist standardmäßig auf SSL3 festgelegt.) Um alle Ihre Grundlagen für das zu verwendende Protokoll abzudecken, können Sie alle definieren. System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Ssl3;

0

Ich bin mit dem gleichen Code-Basis, wie Sie aber statt

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

mit Ich verwende X509KeyStorageFlags.UserKeySet

auch habe ich das das Root-Zertifikat auf die Current/CA installiert und arbeitet für mich.