Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WithClientCertificates Fails with Disposed X509Certificate2 Object (Possible Reference Retention) #2157

Open
uriel-kluk opened this issue Feb 21, 2025 · 0 comments
Labels
bug Something isn't working

Comments

@uriel-kluk
Copy link

Description

When configuring MQTTnet with mTLS using WithClientCertificates, disposing the X509Certificate2 object before MqttClientOptionsBuilder.Build() causes the TLS handshake to fail with a tlsv13 alert certificate required error. This suggests MQTTnet (or underlying .NET SslStream) might retain a reference to the X509Certificate2 object rather than copying its data immediately, leading to issues when the object is disposed early. This behavior is inconsistent with the expectation that certificate data is copied during configuration, as noted in .NET networking patterns.

Environment:

MQTTnet Version: 5.0.1.1416
.NET Version: .NET 8.0
OS: Ubuntu 22.04,
Broker: custom MQTT broker requiring TLS 1.3 mTLS

Steps to Reproduce:

Create an MQTT client with mTLS using a PEM certificate and key and use the keyword using for the X509Certificate2

var mqttClient = new MqttFactory().CreateMqttClient();
var certPem = File.ReadAllText("cert.pem");
var keyPem = File.ReadAllText("key.pem");
using var clientCertificate = X509Certificate2.CreateFromPem(certPem, keyPem);

// Case 1: Dispose before Build() - Fails
using var clientCertificatePfx = new X509Certificate2(clientCertificate.Export(X509ContentType.Pkcs12));
var optionsBuilder = new MqttClientOptionsBuilder()
    .WithTcpServer("broker.example.com", 8883)
    .WithTlsOptions(o => o.WithClientCertificates([clientCertificatePfx]));
var options = optionsBuilder.Build(); // clientCertificatePfx disposed here
await mqttClient.ConnectAsync(options); // Fails

Compare with non-disposed version

var clientCertificatePfx = new X509Certificate2(clientCertificate.Export(X509ContentType.Pkcs12));
var optionsBuilder = new MqttClientOptionsBuilder()
    .WithTcpServer("broker.example.com", 8883)
    .WithTlsOptions(o => o.WithClientCertificates([clientCertificatePfx]));
var options = optionsBuilder.Build(); // clientCertificatePfx still alive
await mqttClient.ConnectAsync(options); // Succeeds

Expected Behavior:

WithClientCertificates should copy the certificate data (public cert and private key) when called, allowing the X509Certificate2 object to be safely disposed before Build() or ConnectAsync(). This aligns with typical .NET networking patterns (e.g., HttpClient with client certificates).

Actual Behavior:

Disposing the X509Certificate2 object before Build() causes the TLS handshake to fail with:

MQTTnet.Adapter.MqttConnectingFailedException: Error while authenticating.
---> Interop+Crypto+OpenSslCryptographicException: error:0A00045C:SSL routines::tlsv13 alert certificate required

Impact:

Forces users to keep X509Certificate2 objects alive longer than necessary, risking memory leaks or requiring manual management.
Breaks idiomatic C# patterns like using for disposable resources, leading to unexpected failures in production code.

Suggested Fix:

Ensure WithClientCertificates deep-copies the certificate data (e.g., raw bytes of cert and key) into MqttClientOptions, avoiding reliance on the original X509Certificate2 object post-configuration.
Alternatively, document this behavior explicitly, warning users against early disposal.

@uriel-kluk uriel-kluk added the bug Something isn't working label Feb 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant