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

Support custom CA chain validation #1851

Merged
merged 3 commits into from Nov 1, 2023
Merged

Conversation

rido-min
Copy link
Member

Summary

Most MQTT clients from Paho (python, Java, GO) and even mosquitto-clients, allow to specify a CaFile to connect to TLS endpoints protected with certificates issued by a private CA.

A good example is test.mosquitto.org:8883 that requires https://test.mosquitto.org/ssl/mosquitto.org.crt to validate the server connection

Details

  • Adds a CertificationAuthoritiesFile option to TlsOptions
  • Adds a WithCertificationAuthoritiesFile(string pemFile) to TlsOptionsBuilder
  • Configures SslStream with CustomTrust (.NET 7+ only)
  • Adds a new sample, including the test.mosquitto.org (this might require to be updated when it gets expired in 2030 :) )

Overseeds #1848

@rido-min
Copy link
Member Author

rido-min commented Oct 2, 2023

How can I fix the build error?

D:\a\MQTTnet\MQTTnet\certificate.snk' -- Invalid public key

#else
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
#endif

#if NET7_0_OR_GREATER
public string CertificationAuthoritiesFile { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend to make this API always visible but throw on the setter for <net7

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other properties in this project are usually not available because they are excluded by the compiler. I would like to stick to this approach and avoid changing the strategy here.

TargetHost = targetHost,
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
};
#if NET7_0_OR_GREATER
if (!string.IsNullOrEmpty(_tcpOptions.TlsOptions.CertificationAuthoritiesFile))
Copy link
Member

@krwq krwq Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another option: File.Exists (error when doesn't exist would be nice but possibly just letting it error later is fine as well, follow existing conventions)

@@ -132,5 +132,13 @@ public MqttClientTlsOptionsBuilder WithCipherSuitesPolicy(EncryptionPolicy encry
return this;
}
#endif

#if NET7_0_OR_GREATER
public MqttClientTlsOptionsBuilder WithCertificationAuthoritiesFile(string pemFile)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another option will be WithCaFile is shorter -not as descriptive as the other methods in this class - but is aligned with other mqtt tooling.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other methods and properties in this project usually take the "long" version or adopt the same name when it is simply mapped to a property in the .NET framework.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is outdated, the last commit changed the signature to: public MqttClientTlsOptionsBuilder WithTrustChain(X509Certificate2Collection chain)

To align with .NET api this could be .WithCustomTrustChain

@logicaloud
Copy link
Contributor

I think it would be good to support handling of certificate authorities directly within the MQTTnet library.

Some observations and questions:

  1. Naming should probably be ...CertificateAuthorities... instead of ...CertificationAuthorities...?
  2. Should the target conditional be NET5_0_OR_GREATER? Or are there features used that are not available since .NET 5? I'm aware that .NET 5 is out of support, but this would be in keeping with other conditionals used elsewhere supporting older .NET versions (and .NET 6 is still in support).
  3. My preference would be to pass the certificate authorities as a list or collection of X509Certificate2 to keep things in memory. Certificates may originate from a configuration file, database, of otherwise different format not readily available as a .crt file on disk. If the certificate authority certificates are indeed already available as a suitable file, then it is a small burden for the MQTTnet library user to load the file into a list or collection of X509Certificate2 certificates for handover to WithCertificateAuthorities. If the the certificates are not already in a suitable file, then passing a filename could become a pain point, because the MQTTnet library user needs to convert the format into the .crt file format and ensure that a there is a writable file system location available just for handover.

@rido-min
Copy link
Member Author

thanks @logicaloud for you comments:

  1. The name was suggested by @CIPop, maybe we could use WithTrustChain ?
  2. IIRC The API we are using to use a custom trust store is available in .NET7+ only, but let me double check if this could also work in .NET5+
  3. Good point, I'm updating the PR to accept a X509CertCollection

@rido-min
Copy link
Member Author

@logicaloud with this 0b5dff5 I've addressed your comments:

  1. Renamed to public MqttClientTlsOptionsBuilder WithTrustChain(X509Certificate2Collection chain)
  2. Unfortunately SslClientAuthenticationOptions.CertificateChainPolicy requires .NET7
  3. Agree, the user should provide the caChain as a cert collection without imposing any crt/pem format in a file

@logicaloud
Copy link
Contributor

Thank you for looking into this, @rido-min. Regarding naming, WithTrustChain seems more technical, WithCertificateAuthorities may need less explanation in the context of MQTT. Either way, it is not up to me to accept pull requests, so best to wait now until @chkr1011 has time to review and comment.

@rido-min
Copy link
Member Author

WithTrustChain seems more technical,

Well, keep in mind this is on top of MqttTlsOptions, so I guess the TrustChain concept will be easier to get.

@rido-min
Copy link
Member Author

apologies for adding another commit cc1ed2c

Now the chain validation uses X509VerificationFlags.IgnoreEndRevocationUnknown so users are not forced to completely disable revocation checks when using custom CAs.

@krwq do you mind taking another look?

@chkr1011 chkr1011 merged commit 01c90ba into dotnet:master Nov 1, 2023
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants