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

AWS support? #927

Closed
stefxx opened this issue May 15, 2020 · 27 comments
Closed

AWS support? #927

stefxx opened this issue May 15, 2020 · 27 comments
Assignees
Labels
bug Something isn't working

Comments

@stefxx
Copy link

stefxx commented May 15, 2020

Describe the bug

Is this a bug? Of not supported? I am trying to connect to AWS IoT with client certificate over port 8883. I see some references to ALPN but that doesn't seem a hard requirement. But I couldn't find anyway to configure ALPN as well, so that might be related.

Which project is your bug related to?

  • Client

Details

I checked that the certificates are valid by using "openssl s_client -CAfile ca.pem -cert client.pem -key client.key -connect your-endpoint.amazonaws.com:8883". Also, I catch the certificate errors in MQTTnet so that all seems fine. But when I try a ConnectAsync, this is the error I receive: "MQTTnet.Exceptions.MqttCommunicationTimedOutException' was thrown".

MQTTnet.Exceptions.MqttCommunicationTimedOutException
  HResult=0x80131500
  Message=Exception of type 'MQTTnet.Exceptions.MqttCommunicationTimedOutException' was thrown.
  Source=mscorlib
  StackTrace:
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MQTTnet.PacketDispatcher.MqttPacketAwaiter`1.<WaitOneAsync>d__4.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MQTTnet.Client.MqttClient.<SendAndReceiveAsync>d__50`1.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MQTTnet.Client.MqttClient.<AuthenticateAsync>d__44.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at MQTTnet.Client.MqttClient.<ConnectAsync>d__35.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at MQTTnet.Client.MqttClient.<ConnectAsync>d__35.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at MyProgram.MyProg.PD() in MyProgram.vb:line 577
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
@stefxx stefxx added the bug Something isn't working label May 15, 2020
@stefxx
Copy link
Author

stefxx commented May 15, 2020

@SeppPenner
Copy link
Collaborator

@chkr1011
Copy link
Collaborator

You can also try to use the WebSocket4Net extension of this lib because WebSocket4Net supports more protocols than the WebSocket implementation in .NET.

@stefxx
Copy link
Author

stefxx commented May 17, 2020

Thanks for the pointers! I did look at the link but this is all about Sigv4, not certificate authentication. Also, I tried to use WebSocket4Net but i can't seem to find any docs or examples on how to use it. What should I do except Dim mqttClient As Client.MqttClient = New MqttFactory().UseWebSocket4Net.CreateMqttClient?

The more I read, the more it seems I need to use the "x-amzn-mqtt-ca" header somehow...

@dxmann
Copy link

dxmann commented Jun 23, 2020

Thanks for the pointers! I did look at the link but this is all about Sigv4, not certificate authentication. Also, I tried to use WebSocket4Net but i can't seem to find any docs or examples on how to use it. What should I do except Dim mqttClient As Client.MqttClient = New MqttFactory().UseWebSocket4Net.CreateMqttClient?

The more I read, the more it seems I need to use the "x-amzn-mqtt-ca" header somehow...

I'm working with the same problem: I want to authenticate in AWS IoT with certificate on port 8883.
When I have prepared the certificate and started the client I have this issue:

Error: MQTTnet.Exceptions.MqttCommunicationException: Authentication failed, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> System.ComponentModel.Win32Exception (0x80090327): Errore sconosciuto durante l'elaborazione del certificato.
   --- 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.ThrowIfExceptional()
   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.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
   at MQTTnet.Implementations.MqttTcpChannel.ConnectAsync(CancellationToken cancellationToken)
   at MQTTnet.Implementations.MqttTcpChannel.ConnectAsync(CancellationToken cancellationToken)
   at MQTTnet.Internal.MqttTaskTimeout.WaitAsync(Func`2 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)
Verbose: MqttClient Disconnecting [Timeout={0}]
Verbose: MqttClient Disconnected from adapter.
Info: MqttClient Disconnected.

I don't think that the problem is in the certificate.pem.crt and privatekey.pen.key released by AWS...
so where's the problem?

@stefxx
Copy link
Author

stefxx commented Jun 23, 2020

I haven't been able to get this working. But if you want to make sure your certificates are fine, you can test it like this:

openssl s_client -CAfile ca.pem -cert client.pem -key client.key -connect your-endpoint.amazonaws.com:8883

If you ever make this work, I am still very interested!

@dxmann
Copy link

dxmann commented Jun 24, 2020

Is useful to setup the rootCA certificate? AWS released 3 main files: dev certificate, dev private key and root CA certificate. How to setup the last one?

Note: I have found this function that takes the dev's certificate and private key and decodes the certificate (it works)

private async Task<X509Certificate2> LoadPemCertificate(string certificatePath, string privateKeyPath)
        {
            using var publicKey = new X509Certificate2(certificatePath);

            var privateKeyText = await File.ReadAllTextAsync(privateKeyPath);
            var privateKeyBlocks = privateKeyText.Split("-", StringSplitOptions.RemoveEmptyEntries);
            var privateKeyBytes = Convert.FromBase64String(privateKeyBlocks[1]);
            using var rsa = RSA.Create();

            if (privateKeyBlocks[0] == "BEGIN PRIVATE KEY")
                rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
            else if (privateKeyBlocks[0] == "BEGIN RSA PRIVATE KEY")
                rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
            else
                throw new ArgumentException("key type not recognized");

            // keyPair contains the decrypted certificate
            var keyPair = publicKey.CopyWithPrivateKey(rsa);
            return new X509Certificate2(keyPair.Export(X509ContentType.Cert));
        }

What can I do now?

@dxmann
Copy link

dxmann commented Jun 24, 2020

I haven't been able to get this working. But if you want to make sure your certificates are fine, you can test it like this:

openssl s_client -CAfile ca.pem -cert client.pem -key client.key -connect your-endpoint.amazonaws.com:8883

If you ever make this work, I am still very interested!

I have performed the test:

openssl s_client -CAfile /.../AmazonRootCA1.pem -cert /.../...-certificate.pem.crt -key /.../....-private.pem.key -connect <endpoint>.iot.<region>.amazonaws.com:8883
CONNECTED(00000003)
...
SSL handshake has read 2393 bytes and written 1608 bytes
Verification error: unable to get local issuer certificate
...

@dxmann
Copy link

dxmann commented Jul 30, 2020

ok, now the verification works: I have to remove the -CAfile argument... it's strange but works

@stefxx
Copy link
Author

stefxx commented Jul 30, 2020

Great find!

Have you resolved the verification in the .Net code as well? I would love to see a working example of that.

@dxmann
Copy link

dxmann commented Jul 30, 2020

My terror was that the error is only in the win implementation of dotnet code... nope...
in linux I have a similar stacktrace: the error is inside the SslStram.Authenticate:

---> 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
... etc ...

but in the configuration I have specified that

var options = new MqttClientOptionsBuilder()
                    .WithClientId(account.ClientId)
                    .WithTcpServer(broker.Host, broker.Port)
                    .WithCleanSession()
                    .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
                    //.WithCommunicationTimeout(TimeSpan.FromSeconds(30))
                    .WithTls(new MqttClientOptionsBuilderTlsParameters
                    {
                        UseTls = true,
                        SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
                        IgnoreCertificateChainErrors = false,
                        IgnoreCertificateRevocationErrors = false,
                        AllowUntrustedCertificates = false,
                        Certificates = new List<X509Certificate> { cert },
                        //CertificateValidationHandler = delegate { return true; }
                    })
                    .Build();

NOTE
Debugging in Visual Studio I can confirm that the protocol that arrives in SslStream is correct: System.Security.Authentication.SslProtocols.Tls12; so why I have an error " Interop+Crypto+OpenSslCryptographicException: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate"?

@dxmann
Copy link

dxmann commented Jul 30, 2020

ah my test with openss was:

openssl s_client -key <dev_id>-private.pem.key -cert <dev_id>-certificate.pem.crt -connect <code>.iot.<region>.amazonaws.com:8883

CONNECTED(00000003)
...
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: <bla>
Shared Requested Signature Algorithms: <bla>
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 2393 bytes and written 1618 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-AES256-GCM-SHA384
    Session-ID: <bla>
    Session-ID-ctx: 
    Master-Key: <bla>
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: <bla>
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---

@dasa-asen
Copy link

I had a similar problem when connecting to AWS IoT. The trick for me was to export and recreate the certificate in the MqttClientOptionsBuilderTlsParameters. Using only new[] { cert } did not work.

Certificates = new[] { new X509Certificate(cert.Export(X509ContentType.Pfx)) },

If there is a better solution, please share it.

@stefxx
Copy link
Author

stefxx commented Aug 14, 2020

Hi @dasa-asen , thanks! Can you post a little more of your code? I still can't get it to work. Some larger example would be helpfull.

Thanks!

@dasa-asen
Copy link

dasa-asen commented Aug 17, 2020

Hi @dasa-asen , thanks! Can you post a little more of your code? I still can't get it to work. Some larger example would be helpfull.

Sure. Here is my code for opening the MQTT/TLS client:

X509Certificate2 cert = Aws.CertificateReader.ReadCertFromZipFile(certificateManager.PackageFile);

// Create a new MQTT client.
var factory = new MqttFactory();
mqttClient = factory.CreateMqttClient();

var tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
    UseTls = true,
    Certificates =  new[] { new X509Certificate(cert.Export(X509ContentType.Pfx)) },
    IgnoreCertificateRevocationErrors = true, //Prevent online revocation check
    IgnoreCertificateChainErrors = disableServerValidation,
    AllowUntrustedCertificates = disableServerValidation,
};

// Create TCP based options using the builder.
var connectOptions = new MqttClientOptionsBuilder()
    .WithClientId(certificateManager.ThingName)
    .WithTcpServer(BrokerHostName, BrokerPort)
    .WithTls(tlsParameters)
    .WithCommunicationTimeout(TimeSpan.FromMilliseconds(connectionTimeout))
    .WithCleanSession()
    .Build();

var conResult = await mqttClient.ConnectAsync(connectOptions);

I receive the certificates and keys in PEM format packed in a single zip file. I unzip, read the PEM data and pass the binary content to the constructor of X509Certificate2.

I am certainly no TLS expert and have come up with this by trial and error. The CertificateValidationHandler specifically should probably be done in some other way, but this worked for me on my targets which is ASP.NET Core 3.1 running on either Windows or an embedded Linux target.

Edit: I removed the CertificateValidationHandler since it made no sense and made use of the ignore/allow options instead. My embedded Linux device were having problems with online revocation which is default. IgnoreCertificateRevocationErrors = true also changes the RevocationMode to NoCheck and that works a lot better for me. Currently I have not found a straight forward way to manually verify the server with a stand alone root certificate, but that is not important for my use case.

@nicolasr75
Copy link

nicolasr75 commented Oct 6, 2020

Thanks guys for sharing your tests. I followed this description https://github.com/aws-samples/iot-dotnet-publisher-consumer
to set up the certs. First I got a timeout. I then realized that I need to set up a policy and assign that policy to the certificate as mentioned here aws-samples/iot-dotnet-publisher-consumer#2

With these settings I can successfully connect and publish a message with MQTTnet to AWS!

@nicolasr75
Copy link

For others trying this I would like to mention two pitfalls that at first prevented me from publishing messages. Quoted from the AWS IoT docs:

AWS IoT supports MQTT quality of service (QoS) levels 0 and 1 only. AWS IoT doesn't support publishing or subscribing with QoS level 2. When QoS level 2 is requested, the message broker doesn't send a PUBACK or SUBACK.

The MQTT specification provides a provision for the publisher to request that the broker retain the last message sent to a topic and send it to all future topic subscribers. AWS IoT doesn't support retained messages. If a request is made to retain messages, the connection is disconnected.

Meaning, don't use .WithExactlyOnceQoS(), .WithRetainFlag()

@Chef904
Copy link

Chef904 commented Mar 17, 2021

Hi,
i am not able to connect with AWS IoT over TLS. Is there anywhere an complete code example with MQTTnet? With M2Mqtt i got an successful connectien.

@jkoplo
Copy link

jkoplo commented Apr 7, 2021

This looks like a duplicate of #721

I've got working code that I'll share, just deciding on the best way to do that.

@albert-t25
Copy link

@Chef904 Having the same problem. Were you able to get it working?

@Chef904
Copy link

Chef904 commented Apr 19, 2021

@albert-t25 No, unfortunately not. I use M2MQTTdotnetcore now. When I have time I might look at the problem again. Sorry 😕

@albert-t25
Copy link

Thank you @Chef904 . I have tried M2MQTTdotnetcore, but it still times out.

@jkoplo
Copy link

jkoplo commented Apr 27, 2021

I answered this in #721 also

MQTTNet to AWS IoT - Core
MQTTNet to AWS IoT - Framework

It's worth noting that these gists also work for M2MQTT. The trick to it all is loading the certs into X509 objects and using a callback for verification (there's a callback for the M2MQTT signature in the helper class).

@albert-t25
Copy link

In my case, it was a silly mistake with AWS regions. Anyone with similar problem check out #1079 (comment)

@DerekCalder
Copy link

In my case, the problem was that I was using the default Thing access policy, which only allows connections from the Java, Node.js, and Python SDKs.

@SeppPenner SeppPenner self-assigned this Aug 30, 2021
@SeppPenner
Copy link
Collaborator

I've collected every troubleshooting tips under https://github.com/chkr1011/MQTTnet/wiki/Client#connecting-with-amazon-aws now. There is a discussion available under #1225 where I'm trying to collect all issues with AWS now since all the same issues seem to occur very often.

@benlongo
Copy link

The aws example in this link https://github.com/chkr1011/MQTTnet/wiki/Client#connecting-with-amazon-aws appears to simply not check the CA and always return true, which doesn't seem secure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests