Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trace TLS in .NET Core

Tags:

.net

.net-core

With .NET framework, we had this in the configuration, so you could trace the System.Net and System.Net.Sockets namespaces, but how can we do it in .NET Core?

I've tried without any luck.

<system.diagnostics>
  <trace autoflush="true"/>
  <sources>
    <source name="System.Net" maxdatasize="1024">
      <listeners>
        <add name="TraceFile"/>
      </listeners>
    </source>
    <source name="System.Net.Sockets" maxdatasize="1024">
      <listeners>
        <add name="TraceFile"/>
      </listeners>
    </source>
  </sources>
  <sharedListeners>
    <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
      initializeData="trace.log"/>
  </sharedListeners>
  <switches>
    <add name="System.Net" value="Verbose" />
    <add name="System.Net.Sockets" value="Verbose" />
  </switches>
</system.diagnostics>
like image 622
user3017827 Avatar asked Oct 24 '25 05:10

user3017827


1 Answers

Low-level tracing of HTTP events isn't the best way to troubleshoot TLS issues because they're too low-level. I suspect the real problem is a mismatch in algorithms, otherwise the exception message would explain what's wrong, eg an untrusted or expired certificate.

An easier option is to use a debugging proxy like Fiddler which captures and translates the TLS handshake, showing what algorithms were requested.

As for tracing, it's evolved quite a bit since .NET Framework and since .NET 6 Tracing can publish to various targets, including OpenTelemetry systems as long as the correct sources are configured. As the linked article shows though, an easier but more expensive solution is to create an EventListener in the code which writes directly to console or ILogger, or anything else we want.

The blog example listens to System.Net.Http events. That's not enough to capture TLS algorithm negotiation though. This Github Issue shows how to listen to the internal Private.InternalDiagnostics.System.Net.Http source. This results in a lot of very detailed messages, but they do include algorithms, thumbnails etc:

using System.Diagnostics;
using System.Diagnostics.Tracing;

internal sealed class HttpEventListener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        // Allow internal HTTP logging
        if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http")
        {
            EnableEvents(eventSource, EventLevel.LogAlways);
        }
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        // Log whatever other properties you want, this is just an example
        var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
        for (int i = 0; i < eventData.Payload?.Count; i++)
        {
            if (i > 0)
                sb.Append(", ");
            sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]);
        }
        try {
            Console.WriteLine(sb.ToString());
        } catch { }
    }
}

With that class you can start listening to low-level events :

var listener=new HttpEventListener();
var hc=new HttpClient();
await hc.GetStringAsync("https://dotnet.microsoft.com/");

This generates a lot of events, but one of them is this

08:34:11.4330369[HandlerMessage] poolId: 27011992, workerId: 6576978, requestId: 0, memberName: TraceConnection,     
message: HttpConnection(HttpConnectionPool https://dotnet.microsoft.com:443). SslProtocol:Tls12,                     
NegotiatedApplicationProtocol:, NegotiatedCipherSuite:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherAlgorithm:Aes256, 
CipherStrength:256, HashAlgorithm:Sha384, HashStrength:0, KeyExchangeAlgorithm:44550, KeyExchangeStrength:256,       
LocalCertificate:, RemoteCertificate:[Subject]                                                                          CN=dotnet.microsoft.com, O=Microsoft Corporation, L=Redmond, S=WA, C=US                                                                                                                                                                 ...

To listen to events from other libraries, modify the check in OnEventSourceCreated. You can find even the private event sources by checking the .NET repo directly. The event source for System.Net.Sockets is defined in NetEventSource.Sockets.cs and its name is Private.InternalDiagnostics.System.Net.Sockets. That means OnEventSourceCreated can be changed to :

var monitoredSources=new[]{
    "Private.InternalDiagnostics.System.Net.Http",
    "Private.InternalDiagnostics.System.Net.Sockets"
};
if (monitoredSources.Contains(eventSource.Name))
{
    EnableEvents(eventSource, EventLevel.LogAlways);
}

Or even this, to record all System.Net diagnostic events

if (eventSource.Name.StartsWith("Private.InternalDiagnostics.System.Net"))
{
    EnableEvents(eventSource, EventLevel.LogAlways);
}
like image 51
Panagiotis Kanavos Avatar answered Oct 26 '25 19:10

Panagiotis Kanavos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!