dotnet / MQTTnet

MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
MIT License
4.51k stars 1.07k forks source link

Open SSL Bad cert on linux: Interop+Crypto+OpenSslCryptographicException: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate #922

Closed Jonesie closed 4 years ago

Jonesie commented 4 years ago

Describe your question

Everything works fine on Windows but when I publish from our test or prod platform I get this error from OpenSSL:

The error occurs when publishing a message from our API. We use AWS IoT Core over HTTPS with TLS.

The API is running in a Fargate container (docker) on Amazon Linux. I have checked that the certs are in the container and are as expected.

I add the certs to the store - because, well, why not - and on the 2nd pass it tells me the certs are in the store, but I just use the version from file.

Ive tried with and without passwords - null or empty string. Every combo I can think of.

Ive been churning on this for days. Im hoping someone here and spot my stupid mistake (95% normal for me).

Thanks

` public async Task Send(Agent agent, IQueueMessage msg) { var json = JsonConvert.SerializeObject(msg); var bytes = System.Text.Encoding.UTF8.GetBytes(json);

  var topic = agent.IoTTopic + "/" + msg.Type;

  var mqmsg = new MqttApplicationMessageBuilder()
    .WithTopic(topic)
    .WithPayload(bytes)
    .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
    .Build();

  CheckConnected();
  await _connection.PublishAsync(mqmsg);
}

private void CheckConnected()
{
  if (_factory == null)
  {
    _factory = new MqttFactory();
  }

  if (_connection == null || !_connection.IsConnected)
  {
    var tlsOptions = new MqttClientOptionsBuilderTlsParameters
    {
      UseTls = true,
      AllowUntrustedCertificates = true,
      SslProtocol = SslProtocols.Tls12,
      Certificates = GetCertsList(),
      CertificateValidationCallback = (X509Certificate x, X509Chain y, SslPolicyErrors z, IMqttClientOptions o) =>
      {
        return true;
      }
    };

    _connection = _factory.CreateManagedMqttClient(new MQTTLogger(_logger));
    var options = new ManagedMqttClientOptionsBuilder()
      .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
      .WithClientOptions(new MqttClientOptionsBuilder()
          .WithClientId(_settings.Value.IoTClientID)
          .WithCommunicationTimeout(TimeSpan.FromSeconds(10))
          .WithTcpServer(_settings.Value.IoTEndpoint, _settings.Value.IoTPort)
          .WithTls(tlsOptions).Build())
      .Build();

    _connection.ConnectedHandler = new MqttClientConnectedHandlerDelegate(async e =>
    {
      _logger.LogInformation("MQTT connected");
    });

    _connection.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate(e =>
    {
      _logger.LogError(e.Exception, "MQTT connection failed");
    });

    _connection.ApplicationMessageProcessedHandler = new ApplicationMessageProcessedHandlerDelegate(e =>
   {
     if (e.HasSucceeded)
     {
       _logger.LogDebug($"MQTT {e.ApplicationMessage.Id} Message published");
     }
     else if (e.HasFailed)
     {
       if (e.Exception != null)
       {
         _logger.LogError(e.Exception, "MQTT Publish failed");
       }
       else
       {
         _logger.LogError("MQTT Publish failed for unknown reason");
       }

     }
   });

    _connection.StartAsync(options).Wait();
  }
}

private List<X509Certificate2> GetCertsList()
{
  return new List<X509Certificate2>
  {
    GetCert(_settings.Value.CARootCertificateFile, null),
    GetCert(_settings.Value.IoTCertificateFile, "mybigsecret")
  };
}

private X509Certificate2 GetCert(string certFileName, string password = "")
{
  try
  {
    var cert = new X509Certificate2(GetCertBytes(certFileName), password, X509KeyStorageFlags.PersistKeySet);
    //if (cert.Verify())
    //{
    //  _logger.LogInformation($"Loaded cert {certFileName} and verifide ok");
    //}
    //else
    //{
    //  _logger.LogError($"Loaded cert {certFileName} and NOT verifide ok");
    //}

    using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser, OpenFlags.ReadWrite))
    {
      if (store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false) == null)
      {
        _logger.LogInformation($"Writing certificate {cert.Thumbprint} to store");
        store.Add(cert);
      }
      else
      {
        _logger.LogInformation($"Certificate {cert.Thumbprint} already in store");
      }
    }
    return cert;
  }
  catch (Exception ex)
  {
    _logger.LogError(ex, $"Invalid certificate {certFileName}");
    throw;
  }
}

private byte[] GetCertBytes(string certFileName)
{
  if (certFileName.StartsWith("~"))
  {
    var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    certFileName = certFileName.Replace("~", basePath);
  }

  if (!File.Exists(certFileName))
  {
    throw new Exception($"Certfile {certFileName} missing.");
  }
  return File.ReadAllBytes(certFileName);
}

` There are no exceptions raised from my checks - cert file exists etc.

Full stace below.

Which project is your question related to?

Nuget version: 3.0.9 .Net Core v 3.1 Web API project

Stack Trace:

MQTTnet.Exceptions.MqttCommunicationException: Authentication failed, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, Byte[] recvBuf, Int32 recvOffset, Int32 recvCount, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteContext& context, ArraySegment1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception) at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest) --- End of stack trace from previous location where exception was thrown --- at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult) at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result) at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult) at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__64_2(IAsyncResult iar) at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization) --- End of stack trace from previous location where exception was thrown --- at MQTTnet.Implementations.MqttTcpChannel.ConnectAsync(CancellationToken cancellationToken) at MQTTnet.Internal.MqttTaskTimeout.WaitAsync(Func2 action, TimeSpan timeout, CancellationToken cancellationToken) at MQTTnet.Adapter.MqttChannelAdapter.ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) --- End of inner exception stack trace --- at MQTTnet.Adapter.MqttChannelAdapter.WrapException(Exception exception) at MQTTnet.Adapter.MqttChannelAdapter.ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) at MQTTnet.Client.MqttClient.ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) at MQTTnet.Client.MqttClient.ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) at MQTTnet.Extensions.ManagedClient.ManagedMqttClient.ReconnectIfRequiredAsync()

Jonesie commented 4 years ago

Switched to using M2MQTT. Problem resolved.