jhalbrecht / XamarinFormsMqttSample

An example mqtt client to demonstrate TLS connection to Mosquitto broker.
5 stars 4 forks source link

M2Mqtt and self signed certificates #4

Closed jhalbrecht closed 5 years ago

jhalbrecht commented 5 years ago

I'm attempting to use M2Mqtt to connect to my mosquitto broker over port 8883 with self signed certificates. Here are some resources I used to create and then test my self signed certificates with mosquitto pub/sub and a python sub client.

There may be clues in this example; aws-samples/iot-dotnet-publisher-consumer

Is the .pfx? extension a requirement of aws or m2mqtt? see the aws example above.

Here is my attempt;


X509Certificate clientCert =
    new X509Certificate2($"c:/jstuff/tls/karla/selfsigned/consoleclient.pfx", "xamarin");

X509Certificate caCert =
    //X509Certificate.CreateFromCertFile("c:/jstuff/tls/karla/selfsigned/karla.redacted.org.crt");
    X509Certificate.CreateFromCertFile($"c:/jstuff/tls/karla/selfsigned/karla.redacted.org.crt");

var _client = new MqttClient("redacted.org", 8883, true, caCert, clientCert, MqttSslProtocols.TLSv1_2);

_client.MqttMsgPublishReceived += _client_MqttMsgPublishReceived;
string clientId = Guid.NewGuid().ToString();
_client.Connect(clientId);
_client.Subscribe(new String[] { "xamtest" }, new byte[] { M2Mqtt.Messages.MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
System.Console.ReadKey();

Comments on this issue would leave a conversation and perhaps inspire someone to write this up in a blog post once this goal is accomplished. I won't say no to a pull request either.

jhalbrecht commented 5 years ago

I did have my mosquitto broker secured with the let's encrypt certificate I used for HTTPS without the self signed cert. It just validated the mosquitto broker cert was valid with it's issuer cert.

mosquitto_pub -h iot.eclipse.org -t xamtest -m "datehello again" -p 8883 --capath /etc/ssl/certs/

Apparently this constructor doesn't exist in M2Mqtt anymore. Bring it back?

MqttClient client = new MqttClient("ppatierno-PC",
    MqttClient.MQTT_BROKER_DEFAULT_SSL_PORT,
    true,
    new X509Certificate(Resources.m2mqtt_ca));

This would work fine with the addition of user and password added. The user and password would be encrypted and out of clear text.

jhalbrecht commented 5 years ago

Seeing this exception

InnerException  {System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)

Perhaps I need to implement a validation call back(s) or install the root self signed certificate in the device? See this answer on a so question. https://stackoverflow.com/a/9983342/884630

jhalbrecht commented 5 years ago

Experimenting with RemoteCertificateValidationCallback and LocalCertificateSelectionCallback I better read the documentation this isn't your average copy paste refactor

https://docs.microsoft.com/en-us/dotnet/api/system.net.security.remotecertificatevalidationcallback?redirectedfrom=MSDN&view=netframework-4.7.2

https://docs.microsoft.com/en-us/dotnet/api/system.net.security.localcertificateselectioncallback?view=netframework-4.7.2

mosquitto log excerpt

1550110891: New connection from 192.168.1.1 on port 8883.
1550110923: OpenSSL Error: error:1417C0C7:SSL routines:tls_process_client_certificate:peer did not return a certificate
1550110923: Socket error on client <unknown>, disconnecting.
X509Certificate clientCert =
    new X509Certificate2($"c:/jstuff/tls/debbie/selfsigned/consoleclient.pfx", "xamarin");

X509Certificate caCert =
    //X509Certificate.CreateFromCertFile("c:/jstuff/tls/karla/selfsigned/karla.jeffa.org.crt");
    X509Certificate.CreateFromCertFile($"c:/jstuff/tls/debbie/selfsigned/debbie.jeffa.org.crt");

        //var _client = new MqttClient("jeffa.org", 8883, true, caCert, clientCert, MqttSslProtocols.TLSv1_2);

        var _client = new MqttClient("jeffa.org", 8883, true, caCert, clientCert, MqttSslProtocols.TLSv1_2,
            MyRemoteCertificateValidationCallback,
            MyLocalCertificateSelectionCallback
            );

        _client.MqttMsgPublishReceived += _client_MqttMsgPublishReceived;
        //string clientId = Guid.NewGuid().ToString();
        string clientId = "xamarin";
        _client.Connect(clientId);
        _client.Subscribe(new String[] { "xamtest" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
        System.Console.ReadKey();
    }
    catch (Exception e)
    {
        System.Console.WriteLine(e.Message.ToString());
    }
}

private static bool MyRemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    System.Console.WriteLine($"SslPolicyErrors {sslPolicyErrors}");
    return true;
}

private static X509Certificate MyLocalCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
    System.Console.WriteLine($"acceptableIssuers {acceptableIssuers}");
    // semi informed WAG
    // return X509Certificate.CreateFromCertFile($"c:/jstuff/tls/debbie/selfsigned/debbie.jeffa.org.crt");

    X509Certificate caCert =
            //X509Certificate.CreateFromCertFile("c:/jstuff/tls/karla/selfsigned/karla.jeffa.org.crt");
            X509Certificate.CreateFromCertFile($"c:/jstuff/tls/debbie/selfsigned/debbie.jeffa.org.crt");
    return caCert;
}
jhalbrecht commented 5 years ago

I added RemoteCertificateValidationCallback we don't need the LocalCertificateSelectionCallback

ProTip: Add the self signed root certificate to the machine running this sample. :-) (ProTip heh heh)

image


private static bool MyRemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    // this was handy while learning and trying to get more detail on policy errors.
    foreach (X509ChainStatus item in chain.ChainStatus)
    {
        Console.WriteLine($"{item.StatusInformation}");
    }

    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    // Do not allow this client to communicate with unauthenticated servers.
    // Console.ReadKey();
    return false;
}