emdeedot
emdeedot

Reputation: 51

How to send AWS iot http request from flutter app using certificates?

I am trying to publish a message to AWS IoT using flutter application.

Based on AWS's documentation I have tried to replicate

curl --tlsv1.2 \
    --cacert Amazon-root-CA-1.pem \
    --cert device.pem.crt \
    --key private.pem.key \
    --request POST \
    --data "{ \"message\": \"Hello, world\" }" \
    "https://IoT_data_endpoint:8443/topics/topic?qos=1"

Here is what I have tried

    final List<int> trustedCertificateBytes =
        (await rootBundle.load('assets/certificates/AmazonRootCA1.pem'))
            .buffer
            .asInt8List();
    final List<int> certificateChainBytes = (await rootBundle.load(
            'assets/certificates/device.pem.crt'))
        .buffer
        .asInt8List();
    final List<int> privateKeyBytes = (await rootBundle.load(
            'assets/certificates/private.pem.key'))
        .buffer
        .asInt8List();
    final data = jsonEncode(
      <String, dynamic>{"message": "hello world"},
    );


    final context = SecurityContext.defaultContext;
    context.setTrustedCertificatesBytes(trustedCertificateBytes);
    context.useCertificateChainBytes(certificateChainBytes);
    context.usePrivateKeyBytes(privateKeyBytes);
    final client = HttpClient(context: context);
    final request = await client.openUrl(
        'POST', Uri.parse("https://$myEndpoint/topics/some_topic?qos=1"));
    request.write(data);

    try {
      final response = await request.close();
      print("success");
      print(response);
      response.transform(utf8.decoder).listen((contents) {
        print(contents);
      });
    } catch (e) {
      print(e);
    }

On first api call, http call is successfull but the response is {"message":"Missing authentication","traceId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"}

On every subsequent call flutter throws this error:

 [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: TlsException: Failure trusting builtin roots (OS Error: CERT_ALREADY_IN_HASH_TABLE(x509_lu.c:356), errno = 0)

Any idea on how do I get it to work?

-- Edited section: The first call issue is solved, added context.setAlpnProtocols(["x-amzn-http-ca"], false); worked like charm. The problem on subsequent call still exists.

Upvotes: 1

Views: 746

Answers (2)

Kernel James
Kernel James

Reputation: 4074

I think it's because you are adding and then re-adding the certs to SecurityContext.defaultContext on subsequent calls.

You could try creating a new SecurityContext to do it each time.

var sec = SecurityContext();
sec.set....

Upvotes: 0

emdeedot
emdeedot

Reputation: 51

OP here.

Setting alpn protocol for the context solved the authentication token issue

...
context.setAlpnProtocols(["x-amzn-http-ca"], false);
...

Then I set up a global flag on whether or not the certificates are already set. If already set, no need to set again.

Global scope

bool setCert = false;

Function scope

if (!setCert){
  // Set certificates & alpn protocol here
  setCert = true;
}

And it solved the second issue.

Upvotes: 1

Related Questions