Creating AWS IoT "things" with policies and certificates on-the-fly

Viewed 3103

I am using NodeJS along with AWS JS SDK and AWS IoT Device JS SDK in order to automatically create a new thing and assign certificates and policies to it once it connects to my server.

I was following "Just-in-Time Registration" article in order to create, register and activate my CA certificate. As far as I can tell, the CA certificate is successfully added to AWS IoT, activated and enabled for auto-registration.

What I don't understand is how is this step performed (quote from mentioned article):

When a device attempts to connect with an X.509 certificate that is not known to AWS IoT but was signed by a CA that was registered with AWS IoT, the device certificate will be auto-registered by AWS IoT in a new PENDING_ACTIVATION state.

How do I make an "attempt" to connect? Since I was using aws-iot-device-sdk-js SDK, with manually created certificates, I was usually connecting my device like this:

const device = deviceModule.device({
  host: 'myendpoint.iot.us-east-1.amazonaws.com',
  region: 'us-east-1',
  keyPath: `certs/${deviceID}.key`,
  certPath: `certs/${deviceID}.pem`,
  caPath: 'certs/rootCA.pem',
  clientId: deviceID,
  baseReconnectTimeMs: 4000,
  keepalive: 30,
  protocol: 'mqtts',
});

But now I don't have certificate and key to include in keyPath and certPath and I cannot instantiate my device without it.

I tried to create certificates myself, using createKeysAndCertificate() from AWS SDK, saving them to disk, attaching a policy manually, attaching a principal manually even tried to mark certificate as "active" manually, something along these lines:

iot.createThing({ thingName: deviceID }, (err, d) => {
  if (err) {
    console.log(err);
  } else {
    allThings[d.thingName] = d;
    iot.createKeysAndCertificate({ setAsActive: true }, (e, c) => {
      if (e) {
        console.log(e);
      } else {
        fs.writeFile(`certs/${deviceID}.pem`, c.certificatePem, (ef, f) => {
          if (ef) throw ef;
        });
        fs.writeFile(`certs/${deviceID}.key`, c.keyPair.PrivateKey, (ef, f) => {
          if (ef) throw ef;
        });
        iot.attachPrincipalPolicy({
          policyName: 'my-testing-policy',
          principal: c.certificateArn,
        }, (ee, cc) => {
          if (ee) {
            console.log(ee);
          } else {
            iot.attachThingPrincipal({
              principal: c.certificateArn,
              thingName: deviceID,
            }, (prerr, prdata) => {
              if (prerr) {
                console.log(prerr);
              } else {
                iot.acceptCertificateTransfer({
                  certificateId: c.certificateId,
                  setAsActive: true,
                }, (ce, cd) => {
                  if (err) {
                    console.log(err);
                  } else {
                    console.log('cert activated.');
                  }
                });
              }
            });
          }
        });
      }
    });
  }
});

But after all this, when I try to publish something I am presented with an error:

Error: unable to get local issuer certificate
    at Error (native)
    at TLSSocket.<anonymous> (_tls_wrap.js:1092:38)
    at emitNone (events.js:86:13)
    at TLSSocket.emit (events.js:185:7)
    at TLSSocket._finishInit (_tls_wrap.js:610:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:440:38)

I also tried to subscribe to specific topic, as mentioned in the same article above, aws/events/certificates/registered/e3f0a30... but I've never seen a single message on that topic...

What am I missing here? How do I trigger device certificate and private key generation properly just by using my Just-in-Time certificate?

1 Answers
Related