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

Certificate based authentication for client #115

Closed
malichishti opened this issue Nov 21, 2017 · 17 comments
Closed

Certificate based authentication for client #115

malichishti opened this issue Nov 21, 2017 · 17 comments

Comments

@malichishti
Copy link

Hi,

I'm trying to use MQTTnet as a client to my broker which requires client certificate to authenticate it. How can i achieve this?

Do you support client authentication based on certificates? Each client will have its own certificate.

Regards,
Ali

@chkr1011
Copy link
Collaborator

Hi,
yes you can add the certificate as a byte array to the client options. In combination with the TLS option set to true the certificate will be used.

Loading is at the moment only described for the server at the Wiki (Server > Using a certificate). But the code is the same as for loading a client certificate (beside that the private key is not part of it).

Best regards
Christian

@malichishti
Copy link
Author

malichishti commented Nov 21, 2017

@chkr1011 Thank you for your reply.

Can you advise how i can setup following configuration:
image

I tried below but it did not work:
image

@chkr1011
Copy link
Collaborator

Please try it with the following code (for loading the cert).

var certificate = new X509Certificate(@"C:\certs\test\test.cer", "");
var certBlob = certificate.Export(X509ContentType.Cert);

Because this is a self signed certificate you probably have to add a certificate handler (described in the WIki) and ensure that it is accepted.

If this also doesn't help please let me know.

@malichishti
Copy link
Author

malichishti commented Nov 22, 2017

@chkr1011 Thank you for your reply, I already tried it, unfortunately it did not work. I have also tried to create a pfx file from .crt and .key still no luck.

var options = new ManagedMqttClientOptionsBuilder()
            .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
            .WithClientOptions(new MqttClientOptionsBuilder()
                .WithClientId("ef3614fca42b32d3")
                .WithTcpServer("test.mosquitto.org", 8884)
                .WithTls(true, true, false
                    , new X509Certificate(@"C:\Work\Temp\xmpro\csr\mosquitto.org.cer", "").Export(X509ContentType.Cert)
                    , new X509Certificate(@"C:\Work\Temp\xmpro\csr\client.pfx", "").Export(X509ContentType.Cert))
                .Build())
            .Build();
            MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;
            MqttTcpChannel.CustomCertificateValidationCallback = (x509Certificate, x509Chain, sslPolicyErrors, mqttClientTcpOptions) =>
            {
                return true;
            };
            var mqttClient = new MqttFactory().CreateManagedMqttClient();
            mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic("measures/ef3614fca42b32d3").Build()).GetAwaiter().GetResult();
            mqttClient.StartAsync(options).GetAwaiter().GetResult();

Error:
image

@chkr1011
Copy link
Collaborator

chkr1011 commented Dec 8, 2017

@malichishti Please try again with nuget package 2.6.0-rc2. There are several hotfixes included for TLS communication.

@chkr1011
Copy link
Collaborator

Version 2.6.0 is now released. If you problem still exists please let me know.
Best regards
Christian

@rwhertenstein2
Copy link

rwhertenstein2 commented Dec 21, 2017

I have the latest code as for 2017-12-18, and I am still running into this error with the

**System.AggregateException: 'One or more errors occurred. (A call to SSPI failed, see inner exception.)'
**
MqttCommunicationException: A call to SSPI failed, see inner exception.
AuthenticationException: A call to SSPI failed, see inner exception.
Win32Exception: An unknown error occurred while processing the certificate

So, what I did is, per the notes here, I have created the pfx filke with the .pem.crt with the private.pem.key. I am wondering if the issue is more in the Cert that than the code. However, the error messages are not helping too much on narrowing this down. Suggestions on what I am doing wrong is appreciated. Below is my base code from the Program file (in Main). This is meant to be a client for testing end to end communication (eventually, I will be pairing this with another client to create a ping-pong test across MQTT)

        const string URL = "aws.remote.server";
        const int PORT = 8883;

        var options = new MqttClientOptionsBuilder()
            .WithClientId("RWH2-CodeSim")
            .WithTcpServer(URL, PORT)
            .WithTls(false, false, false
                     , new X509Certificate(@"Certificates\aws-root-cert.pem", "").Export(X509ContentType.Cert)
                     , new X509Certificate(@"Certificates\AWS_IoT_Cert.pfx", "").Export(X509ContentType.Cert)
            )
            .WithCleanSession()
            .WithKeepAlivePeriod(new TimeSpan(0,0,10))
            .Build();

        MqttTcpChannel.CustomCertificateValidationCallback = (x509Certificate, x509Chain, sslPolicyErrors, mqttClientTcpOptions) =>
        {
            if (mqttClientTcpOptions.Server == URL)
            {
                return true;
            }

            return false;
        };

        var mqtt = new MqttFactory().CreateMqttClient();
        // Set up a callback for incoming messages
        mqtt.ApplicationMessageReceived += OnMqttOnApplicationMessageReceived;

        mqtt.ConnectAsync(options).Wait();

Thanks

Robert

@wangfengqi688
Copy link

Hello all:
You need to merge CRT and key into PFX,like this:
openssl pkcs12 -export -in client.crt -inkey client.key -out client.pfx

and:
Invoke WithTls method input allowUntrustedCertificates = true

@wangfengqi688
Copy link

var options = new MqttClientOptionsBuilder()
.WithClientId(clientID)
.WithTcpServer("192.168.8.4", 8883)
.WithCredentials("mqttuser", "wfq123")
.WithKeepAlivePeriod(new TimeSpan(0, 0, 0, 40))
//.WithWillMessage(message)
.WithTls(true, false, false, File.ReadAllBytes(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ssl", "ca.crt")),
File.ReadAllBytes(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ssl", "client.pfx")))
//.WithCleanSession()
.WithProtocolVersion(MQTTnet.Serializer.MqttProtocolVersion.V311)
.Build();
await mqttClient.ConnectAsync(options);

@martin-weber
Copy link

@rwhertenstein2: I had the same problem.
First I created a pfx as described by @wangfengqi688
Then I had to export the certificates using X509ContentType.SerializedCert instead of X509ContentType.Cert and then it worked as described in #124

See code below:

            var caCert = new X509Certificate(@"cacerts.pem");
            var clientCert = new X509Certificate2(@"client1cert.pfx", @"mypassword");
            var options = new MqttClientOptionsBuilder()
                    .WithClientId(_clientId)
                    .WithTcpServer("127.0.0.1", 8883)
                    .WithKeepAlivePeriod(new TimeSpan(0, 0, 0, 40))
                    .WithTls(true, false, false,  
                        caCert.Export(X509ContentType.SerializedCert),
                        clientCert.Export(X509ContentType.SerializedCert))
                    .WithProtocolVersion(MqttProtocolVersion.V311)
                    .Build();

            var mqttClient = new MqttFactory().CreateMqttClient();
            mqttClient.Connected += MqttClientOnConnected;
            mqttClient.ApplicationMessageReceived += MqttClientOnApplicationMessageReceived;
            mqttClient.ConnectAsync(options).GetAwaiter().GetResult();

            mqttClient.SubscribeAsync(
                new TopicFilterBuilder()
                    .WithTopic(Topic)
                    .WithExactlyOnceQoS()
                    .Build())
                .GetAwaiter().GetResult();

@Leobute
Copy link

Leobute commented May 13, 2018

Hi

My pc must dialogue with Amazon AWS iot.

I followed amazon wizard (node js) to create a connection and certificate for my pc.
It generated for me 4 certificate files. (A cert, 2 keys and a root-ca)

Pippo.cert.pem,Pippo.private.key,Pippo.public.key,Root-ca.crt

If i use raspberry and a Python script, all works correctly . It connects, publish an so on…

In the python script, i see that in the connections options, it uses and pass 3 certificate files.

cert_path = "/home/pi/iot/certs/"
host = "xxxxxxxxx.iot.us-west-2.amazonaws.com"
topic = "Domotica"
root_cert = cert_path + "root-CA.crt"
cert_file = cert_path + "pippo.cert.pem"
key_file = cert_path + "pippo.private.key"

client.tls_set(root_cert,certfile = cert_file,keyfile =   key_file,cert_reqs=ssl.CERT_REQUIRED,tls_version=ssl.PROTOCOL_TLSv1_2,ciphers=None)
client.connect(host, 8883, 60)

OK it works

I need to do the same thing on a windows pc using VB.net (but I have tried also in C++ and it is the same)
On my Visual studio 2017, in my project, I installed the nuget package M2MQTT library by Paolo Patierno.
So, following the connection options and the required parameters, I wrote down the code , JUST to try a connection……….
Here I have noticed that the connection options, accept and works only with 2, TWO certificate and it doesn’t require and use the .KEY file (while python does)
So I could use only the Ca-certificate and the client certificate, NOT the private key.

This is my code ……

Dim Endpoint As String = "xxxxxxxxxxxxxxxx.iot.us-west-2.amazonaws.com"
    Dim Port As Integer = 8883

    Dim clientCert As New X509Certificate("D:\Optoteam\pippo.cert.pem")
    Dim cacert As New X509Certificate("D:\Optoteam\root-ca.crt")

    Dim Client = New MqttClient(Endpoint, Port, True, cacert, clientCert, MqttSslProtocols.TLSv1_2, Nothing)
    Client.ProtocolVersion = MqttProtocolVersion.Version_3_1_1

    Dim connesso As Byte = Client.Connect("TestTestTest")

    If (Client.IsConnected) Then
        Client.Subscribe(New String() {"Domotica"}, New Byte() {MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE})
        Client.Publish("Domotica", Encoding.UTF8.GetBytes("Prova"))
    End If

If i make my program run, it arrives at the connect line and it fails.
I have tried all the nuget packages, went deeply to the connection option of each but I haven’t understood how the hell connecting using TLS and if there are some different libraries that use and accept also a third file..a key file.

It cannot connect to the AWS iot MQTT server (While python with the same certificate does)
I got the error below…..

uPLibrary.Networking.M2Mqtt.Exceptions.MqttConnectionException: 'Exception connecting to the broker'
Win32Exception: The message received was unexpected or badly formatted

if I change the connection instruction with a FALSE instead of a TRUE

Dim Client = New MqttClient(Endpoint, Port, false, cacert, clientCert, MqttSslProtocols.TLSv1_2, Nothing)

I receive this error message.

	StackTrace	"   at uPLibrary.Networking.M2Mqtt.MqttClient.SendReceive(Byte[] msgBytes, Int32 timeout) in c:\Users\ppatierno\Source\Repos\m2mqtt\M2Mqtt\MqttClient.cs:line 1094" & vbCrLf & "   at uPLibrary.Networking.M2Mqtt.MqttClient.SendReceive(MqttMsgBase msg, Int32 timeout) in c:\Users\ppatierno\Source\Repos\m2mqtt\M2Mqtt\MqttClient.cs:line 1117" & vbCrLf & "   at uPLibrary.Networking.M2Mqtt.MqttClient.Connect(String clientId, String username, String password, Boolean willRetain, Byte willQosLevel, Boolean willFlag, String willTopic, String willMessage, Boolean cleanSession, UInt16 keepAlivePeriod) in c:\Users\ppatierno\Source\Repos\m2mqtt\M2Mqtt\MqttClient.cs:line 567" & vbCrLf & "   at uPLibrary.Networking.M2Mqtt.MqttClient.Connect(String clientId, String username, String password, Boolean cleanSession, UInt16 keepAlivePeriod) in c:\Users\ppatierno\Source\Repos\m2mqtt\M2Mqtt\MqttClient.cs:line 512" & vbCrLf & "   at MQTT.Form1.Form1_Load(Object sender, EventArgs e) in C:\Users\Leonardo\Documents\Visual Studio 2017\Projects\MQTT\MQTT\Form1.vb:line 25" & vbCrLf & "   at System.EventHandler.Invoke(Object sender, EventArgs e)" & vbCrLf & "   at System.Windows.Forms.Form.OnLoad(EventArgs e)" & vbCrLf & "   at DevExpress.XtraEditors.XtraForm.OnLoad(EventArgs e)" & vbCrLf & "   at System.Windows.Forms.Form.OnCreateControl()" & vbCrLf & "   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)" & vbCrLf & "   at System.Windows.Forms.Control.CreateControl()" & vbCrLf & "   at System.Windows.Forms.Control.WmShowWindow(Message& m)" & vbCrLf & "   at System.Windows.Forms.Control.WndProc(Message& m)" & vbCrLf & "   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)" & vbCrLf & "   at System.Windows.Forms.Form.WmShowWindow(Message& m)" & vbCrLf & "   at System.Windows.Forms.Form.WndProc(Message& m)" & vbCrLf & "   at DevExpress.XtraEditors.XtraForm.WndProc(Message& msg)" & vbCrLf & "   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)" & vbCrLf & "   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)" & vbCrLf & "   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)"	String

Can you help me ??
Is the problem in the missing parameter options of the private key file ? That famous third file I am talking about ?

Please help me. Thanks a lot

@Leobute
Copy link

Leobute commented May 13, 2018

I have tried everything.....
the connect give me this error
Win32Exception: An unknown error occurred while processing the certificate
I am desperate. Can you help me ???

@zcleghern
Copy link

I am following these comments to work out my own issues for my Xamarin app. I can't seem to export the certificate (the Export method hangs with no error message). I do get this warning:

Mono TODO: X509ContentType.Pfx/Pkcs12 and SerializedCert are not supported

has anyone encountered this, and is there a workaround?

@Leobute
Copy link

Leobute commented Aug 19, 2018 via email

@zcleghern
Copy link

I am using Xamarin and thus my project is on mobile, but thanks!

@kyjak
Copy link

kyjak commented Oct 13, 2020

None of these helped, having same issues. There is no real help from the error message in the library.
And there is still no working example code for a client with tls in the project?!
My snipplet, not working.
var caCert = new X509Certificate("ca.crt"); var clientCert = new X509Certificate("client.pfx","pwd"); // X509Certificate clientKey = new X509Certificate("client.key", "", X509KeyStorageFlags.Exportable); var options = new MqttClientOptionsBuilder() .WithClientId(clientName) .WithTcpServer(url) .WithCredentials("exalab", "exheslo42") .WithTls(new MqttClientOptionsBuilderTlsParameters() { AllowUntrustedCertificates = true, UseTls = true, Certificates = new List<X509Certificate> { caCert, clientCert}, CertificateValidationHandler = delegate { return true; }, IgnoreCertificateChainErrors = true, IgnoreCertificateRevocationErrors = true }) .WithKeepAlivePeriod(new TimeSpan(0, 0, 20)) .WithMaximumPacketSize(1024 * 1024) .WithRequestResponseInformation(true) .WithTcpServer(url, portNum) .WithWillMessage(willMessage) .WithCleanSession(false) .Build();

@jhorgan
Copy link

jhorgan commented Nov 5, 2020

Perhaps this may help. I have two examples, one connecting to an Azure IoT Hub and the other to AWS IoT service. Both use certificates to connect.

Azure IoT. It is important to read this https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-security-x509-get-started and this https://github.com/Azure/azure-iot-sdk-c/blob/master/tools/CACertificates/CACertificateOverview.md first to correctly configure CA certificates for the IoT hub.

Note: the TestDevice02.pfx below is the certificate that was created from "Step 4 - Create a new device" here https://github.com/Azure/azure-iot-sdk-c/blob/master/tools/CACertificates/CACertificateOverview.md
`

        const string server = "<iot hub name>.azure-devices.net";
        const string clientId = "TestDevice02";  // IoT device name
        const string username = "<iot hub name>.azure-devices.net/TestDevice02/?api-version=2018-06-30";

        var cert = new X509Certificate2("TestDevice02.pfx", "password");

        var source = new CancellationTokenSource();
        var token = source.Token;

        var factory = new MqttFactory();
        var mqttClient = factory.CreateMqttClient();

        var mqttOptions = new MqttClientOptionsBuilder()
            .WithTcpServer(server, 8883)
            .WithCredentials(username, (string)null)
            .WithClientId(clientId)
            .WithTls(new MqttClientOptionsBuilderTlsParameters
            {
                UseTls = true,
                Certificates = new List<X509Certificate> { cert }
            })
            .Build();

        mqttClient.ConnectAsync(mqttOptions, token).Wait(token);

        // publish message 
        var topic = "devices/TestDevice02/messages/events/";
        var payload = Encoding.ASCII.GetBytes("Hello IoT Hub");
        var message = new MqttApplicationMessage
        {
            Topic = topic,
            Payload = payload,
            QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
            ContentType = "application/text"
        };

        mqttClient.PublishAsync(message, token).Wait(token);

`
For the Azure IoT DeviceClient, see https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-security-x509-get-started#create-an-x509-device-for-your-iot-hub

AWS IoT. I am less familiar with the setup on AWS, however the important point that got it working was the "Converting device certificate from .pem to .pfx" section from https://github.com/aws-samples/iot-dotnet-publisher-consumer. For convivence here it is:

In order to establish an MQTT connection with the AWS IoT platform, the root CA certificate, the private key of the thing, and the certificate of the thing/device are needed. The .NET cryptographic APIs can understand root CA (.crt), device private key (.key) out-of-the-box. It expects the device certificate to be in the .pfx format, not the .pem format. Hence we need to convert the device certificate from .pem to .pfx.

We'll leverage the openssl for converting .pem to .pfx. Navigate to the folder where all the security artifacts are present and launch bash for Windows 10.

The syntax for converting .pem to .pfx is below:

openssl pkcs12 -export -in iotdevicecertificateinpemformat -inkey iotdevivceprivatekey -out devicecertificateinpfxformat -certfile rootcertificatefile

If you replace with actual file names the syntax will look like below

openssl pkcs12 -export -in certificates\certificate.cert.pem -inkey certificates\certificate.private.key -out certificates\certificate.cert.pfx -certfile certificates\AmazonRootCA1.crt

`

        const string server = "<hostname>.iot.eu-west-1.amazonaws.com";
        const string clientId = "XXXXXXXXXXKELYG82g";
        const string machineId = "0639f7ca-0cfb-4240-a761-XXXXXXXX";
        const string topic = "steam/ewon/" + machineId;

        var caCert = new X509Certificate2("root-CA.crt");
        var clientCert = new X509Certificate2("XXXXXXXXXXKELYG82g.pfx", "password");  // .pfx created as outlined above

        var source = new CancellationTokenSource();
        var token = source.Token;

        var factory = new MqttFactory();
        var mqttClient = factory.CreateMqttClient();

        var mqttOptions = new MqttClientOptionsBuilder()
            .WithTcpServer(server, 8883)
            .WithClientId(clientId)
            .WithTls(new MqttClientOptionsBuilderTlsParameters
            {
                UseTls = true,
                AllowUntrustedCertificates = true,
                Certificates = new List<X509Certificate> { caCert, clientCert }
            })
            .Build();

        mqttClient.ConnectAsync(mqttOptions, token).Wait(token);

        // publish message 
        var payload = Encoding.ASCII.GetBytes(<some json string>);
        var message = new MqttApplicationMessage
        {
            Topic = topic,
            Payload = payload,
            QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
            ContentType = "application/json"
        };

        mqttClient.PublishAsync(message, token).Wait(token);

`

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

No branches or pull requests

9 participants