DEV Community

Daan Acohen
Daan Acohen

Posted on

Trace Whether Your .NET `HttpClient` Calls Are Quantum-Safe (PQC) 🚦

Post-quantum cryptography (PQC) has a reputation for sounding “future-ish” and theoretical—until you realize the uncomfortable reality:

Data you encrypt today can be harvested today and decrypted later when a sufficiently capable quantum computer arrives. This is the “harvest now, decrypt later” problem, and it’s the reason PQC has moved from academic curiosity to real-world migration work.

As .NET developers, we ship software that talks to lots of endpoints—internal services, SaaS APIs, identity providers, payment gateways, telemetry backends. And most of those calls happen through one thing:

âś… HttpClient

If PQC readiness matters to your organization (and it increasingly does), you need a simple answer to a simple question:

Which of my outgoing HTTPS calls are already negotiating quantum-resistant key exchange—and which are not?

That’s exactly what PqcTracer provides: it makes TLS negotiation details visible, so you can see whether connections are using post-quantum / hybrid key exchange (for example, groups like X25519MLKEM768).


Why PQC matters (in practical developer terms)

Most TLS connections today rely on key exchange mechanisms based on classical cryptography (commonly elliptic curve or RSA). The concern is that Shor’s algorithm on a sufficiently powerful quantum computer would break RSA and elliptic-curve cryptography, undermining the security assumptions behind today’s TLS handshakes.

The industry response is Post-Quantum Cryptography: algorithms designed to resist both classical and quantum attacks. A particularly important piece is key establishment. One of the NIST-standardized directions here is ML-KEM (formerly known as Kyber), used in modern TLS stacks as part of a hybrid key exchange—combining classical + post-quantum components.

In practice, if you see a negotiated TLS group like:

  • X25519MLKEM768

…you’re looking at a hybrid that includes ML-KEM, i.e., a strong indicator that the connection is negotiating in a way designed to be quantum-resistant.

But here’s the engineering problem: you usually don’t see this from application code.


The typical .NET situation: “TLS happens somewhere below me”

In .NET, outgoing HTTPS calls go through HttpClient, and TLS negotiation is handled by platform crypto + the underlying TLS library. Your app typically doesn’t expose:

  • Which key exchange group was negotiated
  • Which cipher suite was chosen

You can reach for packet capture tooling, but:

  • it’s heavy for day-to-day dev,
  • often not feasible in production,
  • and not the kind of visibility you want to bolt onto every service.

You want something code-level, repeatable, and easy to wire in.


Enter PqcTracer: TLS trace headers for your outgoing HttpClient calls

PqcTracer is a .NET library that traces TLS negotiation details for both incoming and outgoing HTTPS traffic. This article focuses on the part most applications care about immediately:

âś… Outgoing traffic (HttpClient)

Once enabled, you can extract TLS trace info from the response and answer:

  • What TLS group did we negotiate?
  • What cipher suite did we use?
  • Does the group indicate ML-KEM (and therefore PQC/hybrid readiness)?

Install

Use the NuGet package:

https://www.nuget.org/packages/ConnectingApps.PqcTracer

dotnet add package ConnectingApps.PqcTracer
Enter fullscreen mode Exit fullscreen mode

Project source:

https://github.com/ConnectingApps/PqcTracer


Outgoing traffic: two ways to trace TLS on HttpClient

PqcTracer gives you two approaches, depending on whether you build HttpClient manually or rely on DI with IHttpClientFactory.

Both produce the same trace output.


Approach 1: Direct handler usage (quick, explicit, great for utilities)

This is the simplest possible wiring: create a TlsTracingHandler, pass it into HttpClient, and then query the trace from the response.

Exact example code (as documented):

using ConnectingApps.PqcTracer;

using var handler = new TlsTracingHandler();
using var client = new HttpClient(handler);

try
{
    using var response = await client.GetAsync("https://www.google.com");
    response.EnsureSuccessStatusCode();

    var trace = response.GetTlsTrace();
    if (trace != null)
    {
        Console.WriteLine($"Negotiated Group: {trace.Group}");
        Console.WriteLine($"Cipher Suite: {trace.CipherSuite}");
    }
    else
    {
        Console.WriteLine("TLS Trace not found.");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
Enter fullscreen mode Exit fullscreen mode

When to use it

  • Small tools / diagnostics
  • One-off probes in CI
  • Console apps that verify endpoints
  • Situations where you want zero DI setup

Approach 2: DI with IHttpClientFactory (production-friendly)

If you are building services the “modern .NET way”, you likely use IHttpClientFactory already (named clients, typed clients, policies, resilience, handler lifetimes). PqcTracer supports that flow via an extension method:

âś… AddTlsTracing()

Exact example code (as documented):

using ConnectingApps.PqcTracer;
using Microsoft.Extensions.DependencyInjection;

// Register the HttpClient via DI
var services = new ServiceCollection();
services.AddHttpClient("GoogleClient").AddTlsTracing();

var serviceProvider = services.BuildServiceProvider();

// Resolve IHttpClientFactory from the service provider
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();

// Create the client using the factory
using var client = httpClientFactory.CreateClient("GoogleClient");

try
{
    using var response = await client.GetAsync("https://www.google.com");
    response.EnsureSuccessStatusCode();

    var trace = response.GetTlsTrace();
    if (trace != null)
    {
        Console.WriteLine($"Negotiated Group: {trace.Group}");
        Console.WriteLine($"Cipher Suite: {trace.CipherSuite}");
    }
    else
    {
        Console.WriteLine("TLS Trace not found.");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
Enter fullscreen mode Exit fullscreen mode

Why this is usually the right choice

  • Fits naturally into ASP.NET Core apps
  • Works with named/typed clients
  • Plays well with established HttpClient lifetime management
  • Easy to apply selectively (trace only specific clients)

Interpreting the output: what you’re actually looking for

Example output shown in the README (on a system without PQC-capable OpenSSL) is:

Negotiated Group: x25519
Cipher Suite: TLS_AES_256_GCM_SHA384
Enter fullscreen mode Exit fullscreen mode

The key line is the group:

  • x25519 → classical ECDH (not PQC)
  • X25519MLKEM768 → hybrid classical + ML-KEM (PQC-oriented)

In other words, with PqcTracer in place, you can programmatically (or operationally) identify which outbound connections are already negotiating hybrid/PQC groups and which are not.


Why this NuGet package is useful (beyond “cool demo output”)

In real systems, PQC isn’t a switch you flip once. It’s a migration:

  • Some endpoints support hybrid groups, others don’t.
  • Some environments have the right TLS stack, others lag behind.
  • Third-party APIs may roll out support gradually.
  • You need visibility per call path, not just “we upgraded something”.

PqcTracer turns that uncertainty into data. It enables:

  • PQC compliance auditing across outbound API calls
  • Migration tracking over time (are we improving week over week?)
  • Security monitoring (are critical calls still classical-only?)
  • Debugging TLS negotiation without packet captures

The best part is that it’s application-integrated: you instrument your actual HttpClient usage, not a synthetic test harness that drifts away from reality.


Platform note (important before you try it)

PqcTracer currently targets Linux, using OpenSSL 3.x via P/Invoke to extract negotiated TLS group details.

Also, if you want to see ML-KEM groups, you need a PQC-capable OpenSSL version (the README notes OpenSSL 3.5.0 or later for ML-KEM support).


Wrap-up

If PQC is on your roadmap (or on your auditor’s checklist), you do not want to “assume TLS is fine.”

With PqcTracer, you can:

  • instrument outgoing HttpClient traffic,
  • extract the negotiated TLS group and cipher suite,
  • and identify which connections are already negotiating hybrid/PQC key exchange.

Start here:

And then do the satisfying part: run your service, call your real dependencies, and finally see what’s happening in your TLS handshakes.

Top comments (0)