2

I'm trying to establish mtls connection using https://github.com/chkr1011/MQTTnet library in my xamarin.android project. During this process I encountered with such an exception in Init method on calling conResult.Wait(TimeSpan.FromSeconds(60));

{MQTTnet.Exceptions.MqttCommunicationException: Authentication failed, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> System.Security.Cryptography.CryptographicException: Caught…} {System.Security.Cryptography.CryptographicException: Caught unhandled exception in MonoBtlsSslCtx.ProcessHandshake. ---> System.NullReferenceException: Object reference not set to an instance of an object. at Mono.Btls.MonoBtlsSsl.SetPrivateKey (Mono.Btls.…}

This is my code:

public void Init()
    {        
        ILoadCertificate certificateService = DependencyService.Get<ILoadCertificate>();
        var cert = certificateService.LoadPemCertificate("certificate", "private_key");
        Console.WriteLine(cert.GetRSAPrivateKey());
        string clientId = Guid.NewGuid().ToString();
        string mqttURI = "";
        int mqttPort = 8883;

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

        bool disableServerValidation = true;
        var tlsParameters = new MqttClientOptionsBuilderTlsParameters
        {
            UseTls = true,
            Certificates = new[] { new X509Certificate(cert.Export(X509ContentType.Cert)) },
            IgnoreCertificateChainErrors = disableServerValidation,
            AllowUntrustedCertificates = disableServerValidation,
            SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
            CertificateValidationHandler = (o) =>
            {
                return true;
            },
        };

        var connectOptions = new MqttClientOptionsBuilder()
            .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
            .WithClientId(clientId)
            .WithTcpServer(mqttURI, mqttPort)
            .WithCommunicationTimeout(new TimeSpan(0, 2, 30))
            .WithCleanSession()
            .WithTls(tlsParameters)
            .Build();

        var conResult = mqttClient.ConnectAsync(connectOptions);
        conResult.ContinueWith(r =>
        {
            Console.WriteLine(r.Result.ResultCode);
            Console.WriteLine(r.Exception.StackTrace);
        });
        conResult.Wait(TimeSpan.FromSeconds(60));
        var t = mqttClient.PublishAsync("events/test", "test");
        t.ContinueWith(r =>
        {
            Console.WriteLine(r.Result.PacketIdentifier);
            Console.WriteLine(r.Exception.StackTrace);
        });
        t.Wait();
    }

//This methods is used to construct certificate:

public X509Certificate2 GetCertificate(string pemCert, string pemKey)
    {
        string fileNameCert = Path.Combine(Environment
        .GetFolderPath(Environment.SpecialFolder.LocalApplicationData), pemCert);
        var pem = File.ReadAllText(fileNameCert);

        string fileNameKey = Path.Combine(Environment
        .GetFolderPath(Environment.SpecialFolder.LocalApplicationData), pemKey);
        var key = File.ReadAllText(fileNameKey);

        var keyPair = (AsymmetricCipherKeyPair)new PemReader(new StringReader(key))
       .ReadObject();
        var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new 
        StringReader(pem)).ReadObject();

        var builder = new Pkcs12StoreBuilder();
        builder.SetUseDerEncoding(true);
        var store = builder.Build();

        var certEntry = new X509CertificateEntry(cert);
        store.SetCertificateEntry("", certEntry);
        store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });

        byte[] data;
        using (var ms = new MemoryStream())
        {
            store.Save(ms, Array.Empty<char>(), new SecureRandom());
            data = ms.ToArray();
        }

        return new X509Certificate2(data);
    }

public byte[] GetBytesFromPEM(string pemString, string type)
    {
        string header; string footer;
        switch (type)
        {
            case "cert":
                header = "-----BEGIN CERTIFICATE-----";
                footer = "-----END CERTIFICATE-----";
                break;
            case "key":
                header = "-----BEGIN RSA PRIVATE KEY-----";
                footer = "-----END RSA PRIVATE KEY-----";
                break;
            default:
                return null;
        }

        int start = pemString.IndexOf(header) + header.Length;
        int end = pemString.IndexOf(footer, start) - start;
        return Convert.FromBase64String(pemString.Substring(start, end));
    }

I have several options:

  1. Maybe there is some issue with constructing certificate in GetCertificate method;
  2. There is issue with MqttNet library itself. I suspect that the library does not work with certificates in xamarin.android, because i found such topics: https://github.com/xamarin/xamarin-android/issues/4481, https://github.com/chkr1011/MQTTnet/issues/883.

I tried to construct certificate this way, but xamarin does not support rsa.ImportRSAPrivateKey. I have got: System.PlatformNotSupportedException: Operation is not supported on this platform.

string fileNameCert = Path.Combine(Environment
    .GetFolderPath(Environment
    .SpecialFolder.LocalApplicationData), certificatePath);

using var publicKey = new X509Certificate2(fileNameCert);

string fileNameKey = Path.Combine(Environment
     .GetFolderPath(Environment
     .SpecialFolder.LocalApplicationData), privateKeyPath);

 using var rsa = RSA.Create();
 byte[] keyBuffer = 
 GetBytesFromPEM(File.ReadAllText(fileNameKey), "key");

 int o;
 rsa.ImportRSAPrivateKey(keyBuffer, out o);

 var keyPair = publicKey.CopyWithPrivateKey(rsa);
 return new X509Certificate2(keyPair.Export(X509ContentType.Pkcs12));
Yurii
  • 21
  • 2
  • Is this a self-signed cert? – SushiHangover Nov 13 '20 at 08:56
  • I obtain this certificate from AWS. I have two files with -----BEGIN CERTIFICATE-----ddsbsb-----END CERTIFICATE----- and -----BEGIN RSA PRIVATE KEY-----dvdsbrs-----END RSA PRIVATE KEY-----. And as i understand from this pems i need to create X509Certificate2. – Yurii Nov 13 '20 at 11:41

0 Answers0