0

Gegeben: zwei C# -Konsolen-Apps auf verschiedenen Rechnern. Einer ist ein Client, einer ist ein Server. Der Client verwendet HttpClient.SendAsync, um eine Anforderung an den Server zu senden.C# request.GetClientCertificate() friert ein, wenn eine PUT/POST-Anforderung mit größerer Nutzlast verarbeitet wird

Jede an den Server gesendete Anfrage enthält ein Clientzertifikat, das an den Anforderungshandler angehängt wird, und jedes Mal, wenn der Server das Zertifikat zur Überprüfung mit GetClientCertificate() erhält.

Normalerweise funktioniert alles gut: alle GET-Aufrufe sowie PUT-Aufrufe mit geringer Payload (~ 4kb) durchlaufen konsequent.

Wenn Sie jedoch eine PUT-Anforderung mit einer größeren Nutzlast (z. B. ~ 40kb) ausführen, ruft GetClientCertificate() den serverseitigen Block auf und kehrt erst nach 2 Minuten zurück (was unsere Zeitüberschreitungsperiode ist). Das zurückgegebene Zertifikat ist null. Der Client meldet einen Fehler "SSL/TLS-Kanal kann nicht eingerichtet werden".

Es scheint, dass GetClientCertificate() einige zusätzliche Kommunikation, die ich nicht verstehe, und möchte einige Informationen darüber, was könnte Zeitüberschreitung sein. Zum Beispiel haben wir versucht, die Cert-Sperrung auf dem Server zu deaktivieren, aber das hat nicht geholfen. Ich frage mich auch, ob es für einen zwischengeschalteten Netzwerkknoten möglich ist, das Zertifikat zu löschen, und wie dies diagnostiziert werden kann.

Auch dieser Fehler passiert nicht konsequent. Manchmal kann es ein paar Dutzend Mal funktionieren, aber wenn es anfängt zu versagen, wird es weiterhin versagen. Dies könnte auf eine Art von Ressourcenlecks hindeuten, und es wäre schön zu wissen, was bei diesem Szenario passieren könnte. Wir verfügen über HTTP-Client, Handler und schließen den Zertifikatsspeicher.

Ich habe ähnliche Fragen zu StackOverflow gesehen, bei denen das Problem bestand, dass ASP.NET eine Konfiguration erfordert, die auf einem erfassten Kontext nicht fortgesetzt werden muss. Ich frage mich, ob ähnliche Bedenken auch auf Konsolen-Apps zutreffen könnten.

Danke für Ihre Hilfe!

+0

Sie müssen uns mehr über Ihren Server erzählen. Es ist nicht klar, wie Sie dienen, obwohl es wahrscheinlich ist, dass es über 'HttpListener' ist. Vermutet du, was du willst? – spender

+0

@spender, danke, ich hätte es tatsächlich geben sollen.Ich wollte die Frage so spezifisch wie möglich stellen und mich auf GetClientCertificate konzentrieren, ohne Sie irrelevanten Code durchlesen zu lassen, aber ich sehe Ihren Punkt jetzt. Wir verwenden OwinHttpListener und dann ruft Owin WebApp.Start() auf. Der Code ist ziemlich groß und schwer in einer Weise einzufügen, die sinnvoll wäre, aber wenn Sie Daten darüber benötigen, wie bestimmte Schritte ausgeführt werden oder welche Konfigurationseinstellungen vorgenommen werden, werde ich diese auf jeden Fall bereitstellen. – user2520968

Antwort

2

Sie müssen zuerst die POST-Daten lesen, bevor Sie GetClientCertificate() anrufen.

Auch die Aktivierung der Client-Zertifikat-Verhandlung auf der SSL-Zertifikat-Bindung des Servers kann eine Option für Sie sein (aber es wird jede Anfrage veranlassen, nach einer zu fragen).

Führen Sie netsh http show sslcert auf Ihrem Server und suchen Sie nach dem Zertifikat, das an den betreffenden Port gebunden ist, und sehen Sie sich die Negotiate Client Certificate Eigenschaft an. Wenn Sie den Wert in Enabled ändern, wird die Clientzertifikatsverhandlung zu Beginn jeder Verbindung aufgerufen. Wenn Ihr Code also GetClientCertificate() aufruft, ist er bereits vorhanden.

Hier ist ein Code, der die POST-Daten liest, bevor auf das Client-Zertifikat zugegriffen wird. In meinen Tests funktioniert das zuverlässig, aber wie ich im Kommentar angemerkt habe, ist es vielleicht nicht ideal für alle Anwendungsfälle.

private static void ProcessPostRequest(System.Net.HttpListenerContext context) 
{ 
    // Read the entire POST data into a byte array. This isn't ideal. Also, it does not supported 'chunked' encoding. 
    byte[] input  = new byte[context.Request.ContentLength64]; 
    int bytesRead = 0; 
    int totalBytes = 0; 

    while ((bytesRead = context.Request.InputStream.Read(input, totalBytes, Math.Min(4096, input.Length - totalBytes))) > 0) 
    { 
     totalBytes += bytesRead; 
    } 

    System.Diagnostics.Debug.WriteLine($"Read {totalBytes:#,##}"); 

    var cert = context.Request.GetClientCertificate(); 

    if (cert != null) 
    { 
     System.Diagnostics.Debug.WriteLine(cert.Subject); 
    } 

    // Be sure to write to or close the Response. 
} 
1

Bitte lesen Sie diese article. Es hat den Grund und die Empfehlungen für die Behandlung solcher Fälle. Kurz gesagt, Kevins Vorschlag, den Körper vor dem Lesen des Client-Zertifikats zu lesen, sollte das Problem lösen.

Verwandte Themen