rumqtt
rumqtt copied to clipboard
rumqttc: add TLS using OpenSSL, without going through `native-tls`
Hey,
I know this seems like an unusual feature request, because rumqttc
already supports OpenSSL TLS using the native-tls
.
However, in some edge case, I encountered while working with rumqttc
and Azure IoT, I was forced to fork rumqtt and add the OpenSSL TLS option.
The edge case I encountered was the following. I have an Azure IoT Hub instance set up, and wanted to authenticate my devices using X.509 Client authentication. On the device, I used the Azure IoT Identity Service that manages the keys and certificates that my applications running on the device will use. Now the part where the trouble began. If your application authenticates using the IoT Identity Service, it will never get access to the private key. Instead, you only get a Key Hande / Key ID which can then be used using an OpenSSL Engine.
That means I have a raw openssl::PrivateKey
which can not be represented in DER format. So I need a TlsConfiguration::OpenSSL(openssl::ssl::SslConnector)
(or something similar), where I can provide the raw OpenSSL private key to use for client authentication.
Proposed Solution
Add a use-openssl
feature, that will add a TlsConfiguration::OpenSsl
variant.
In the variant one can provide a openssl::ssl::ConnectConfiguration
(or something else, this can be discussed).
This allows the user to use a raw openssl::pkey::PKey
for client authentication.
I understand that this is an edge case, and you might not want to add this to rumqttc, which I totally understand. However, it would be really nice for me since then I don't have to use a custom fork :)
Hey, I had some doubts:
- wdym by "raw
openssl::PrivateKey
" ? -
pkey::PKey
needs to be created somehow right? how would you create it? - will using
rustls
instead ofnative-tls
help?
thanks :)
Hi,
Sorry it might have been badly phrased.
What I mean is, that I have a PKey<Private>
that is constructed by an OpenSSL engine (using with this method).
Resulting from this, the PKey
is not able to be serialized to DER format, thus can not be passed into TlsConfiguration::SimpleNative
.
Rustls wouldn't help because I need to use an OpenSSL engine for the private key operations.
It's a rare edge case, I know!
but it does have a function to convert into der: private_key_to_der
Other conversations are there as well! So i believe you can convert it to der for native-tls, or even to pemfile for rustls.
What I mean is, that I have a PKey<Private> that is constructed by an OpenSSL engine (using with this method).
can you please share example rust code? I'm confused as PKey doesn't have any method corresponding to ENGINE_load_private_key
method 😅 just curious haha
but it does have a function to convert into der: private_key_to_der
Other conversations are there as well! So i believe you can convert it to der for native-tls, or even to pemfile for rustls.
If you call that with a PKey
that comes from an OpenSSL engine, it wil lfail.
What I mean is, that I have a PKey that is constructed by an OpenSSL engine (using with this method).
can you please share example rust code? I'm confused as PKey doesn't have any method corresponding to
ENGINE_load_private_key
method 😅 just curious haha
Unfortunaly, the openssl
crate does not implement bindings for Engines. But you can have a look at this crate where they implement bindings for OpenSSL engines.
If you call that with a PKey that comes from an OpenSSL engine, it wil lfail.
Any reasons to fail? also, does converting to pemfile / other formats fail as well?
Unfortunaly, the openssl crate does not implement bindings for Engines. But you can have a look at this crate where they implement bindings for OpenSSL engines.
Okie! Thanks for sharing.
Any reasons to fail? also, does converting to pemfile / other formats fail as well?
Yes it does, because the key is not a real private key, instead it's some unique ID and an "Engine". The Engine can then use the ID to perform all the signing operations.
A more common use-case of OpenSSL Engines are Yubikeys / HSMs. These are implemented using OpenSSL engines.
Here are some examples for yubikeys
because the key is not a real private key, instead it's some unique ID and an "Engine".
Job of engine should be done by we get the pkey right? As per the crate you shared which called ENGINE_load_key, it uses that ID & engine and returns pkey::Pkey<Private>
, which should be the actual key ( EVP_KEY
).
Can you please confirm once what errors are you getting? And is there a way to reproduce this issue to debug further?
Thank you so much 😁
Hey @swanandx,
sorry for the long delay here, but I totally forgot I had this issue and we were fine using a small custom fork of rumqttc for our needs.
Job of engine should be done by we get the pkey right? As per the crate you shared which called ENGINE_load_key, it uses that ID & engine and returns pkey::Pkey<Private>, which should be the actual key ( EVP_KEY ).
Yes that's right, the engine establishes the private key, however, the private key can not be serialized, as it may be stored on an HSM and be inaccessible for the user. So the engine's job is to overwrite all sign / encrypt / etc operations to use the underlying HSM. That's the reason functions like serializing to DER will fail, and without the possibility to serialize it, it's impossible to pass into the TlsConfiguration::Native
/ TlsConfiguration::SimpleNative
configuration.
Anyway, I actually found another use case for which I still need my custom fork at the moment. My MQTT server has a custom, self-signed root certificate, which I'm unable to mark as trusted in the standard rumqttc
version. You could achieve this using native-tls
, which would be fine too.