Search code examples
aws-iottoit

AWS IoT MQTT with Toit


How do you connect to AWS IoT and pub/sub MQTT messages using Toit?

I was able to follow the MQTT tutorial on the Toit Docs site (https://docs.toit.io/tutorials/mqtt/#tls), which worked great for non-TLS between my ESP32 running Toit and a Raspberry Pi running mosquitto.

However, I am now trying to to pub/sub messages with AWS IoT Core. As instructed in the TLS section of the Toit MQTT tutorial I have installed the Toit root certs package:

jag pkg install github.com/toitware/toit-cert-roots@v1

I changed --root_certificates=[certificate_roots.ISRG_ROOT_X1] to --root_certificates=[certificate_roots.AMAZON_ROOT_CA_1] when creating the transport. However, it is not clear how or where to specify the device certificate and the private key.

In the Toit MQTT Library documentation (https://pkg.toit.io/github.com/toitware/[email protected]/docs/mqtt/class-TcpTransport), it shows how you can specify a parameter called --certificate of type Certificate.

tls network/Interface --host/string --port/int= --root_certificates/List= --server_name/string= --certificate/Certificate= -> TcpTransport
  • Is this where you specify the device cert and private key?
    • If so, how do you create a Certificate object?
  • Do you specify --certificate two times?

Solution

  • You should have a client certificate and a client key. These are typically given by Amazon as "device_name.cert.pem", and "device-name.private.key"

    The certificate starts with -----BEGIN CERTIFICATE----- and the key with -----BEGIN RSA PRIVATE KEY-----.

    Let's assume that the certificate string is in variable CLIENT_CERTIFICATE_DER and the key in variable CLIENT_KEY_DER. The "DER" stands for the "distinguished encoding rules".

    You can then create a transport for AWS as follows:

    import certificate_roots
    import net
    import net.x509
    import mqtt
    import mqtt.transport as mqtt
    import tls
    
    HOST ::= "<YOUR AMAZON HOST>"
    PORT ::= 8883
    ROOT_CERTIFICATE ::= certificate_roots.AMAZON_ROOT_CA_1
    
    CLIENT_CERTIFICATE_DER ::= """
    -----BEGIN CERTIFICATE-----
    <YOUR CERTIFICATE>
    -----END CERTIFICATE-----
    """
    
    CLIENT_KEY_DER ::= """
    -----BEGIN RSA PRIVATE KEY-----
    <YOUR KEY>
    -----END RSA PRIVATE KEY-----
    """
    
    create_aws_transport network/net.Interface -> mqtt.Transport:
      parsed := x509.Certificate.parse CLIENT_CERTIFICATE_DER
      client_certificate := tls.Certificate parsed CLIENT_KEY_DER
      return mqtt.TcpTransport.tls network
          --host=HOST
          --port=PORT
          --root_certificates=[ROOT_CERTIFICATE]
          --certificate=client_certificate
    

    You can then connect to the broker as follows:

    main:
      network := net.open
      transport := create_aws_transport network
      client := mqtt.Client --transport=transport
      options := mqtt.SessionOptions
          --client_id="<YOUR CLIENT ID>"
      client.start --options=options
      client.publish "YOUR_TOPIC" "hello".to_byte_array
      client.close
      network.close
    

    Remember that AWS has policies for each device that restrict the allowed client ids and the allowed topics.

    If you get an "invalid packet kind: 0", then you are using a client_id that isn't allowed by your policy.