For context, check the intro post.
Summary: How to use Signed Certificates in an IoT supply chain. Without giving away your private keys to your subcontractor.
At the end of this post, you completed 90% of the end-to-end process.
You, as subcontractor Louisiana Branch, will generate and sign certificates for Things, on behalf of the conglomerate United States Manufacturing.
Program the Thing
Your subcontractor will load the firmware on your device, install the certificates, provision the device on the cloud and test.
You have developed the firmware and handed it over to the vendor.
When they program it and load the certificates, they may also have to do some configurations.
Most vendors will script all of this, from certificate generation over firmware configuration to the load, provisioning and test, and do this as a step on the assembly line.
In this blog, I'll do it manually.
Upload the certificate artifacts. This is highly dependent on the device you have.
Some provide a dedicated memory area that can only be read by firmware.
Some have a key vault.
On Linux, place it in a folder that's only readable by the user that runs the firmware (here: the Node-RED service user).
In this scenario, we need the tree artifacts generated in the previous post.
Configure and load the firmware. In our case a json file with a Node-RED flow.
The automated script would automatically configure customer ID (cpId), unique ID, and if we use unique names for certificates, also those.
Then import in Node-RED.
[ { "id":"38356517.efc10a", "type":"tab", "label":"IoTConnect New Orleans", "disabled":false, "info":"" }, { "id":"5139ef2a.05a86", "type":"IoTConnect", "z":"38356517.efc10a", "cpId":"1*****", "uniqueId":"neworleans", "env":"PROD", "auth":"2", "offlineStorageEnable":false, "sizeLimit":0, "fileCount":0, "sslKey":"/home/avnet/.iotconnect/.neworleans/1******-neworleans.key.pem", "sslCert":"/home/avnet/.iotconnect/.neworleans/1******-neworleans-chain.cert.pem", "sslCA":"/home/avnet/.iotconnect/.neworleans/ca.cert.pem", "idScope":"", "x":675, "y":200, "wires":[ [ "61178473.a0f70c", "e6dd8afb.945498" ], [ "c56a4d18.5d2ea" ], [ ] ], "l":false }, { "id":"2196de99.bb4322", "type":"IoTConnect-Injector", "z":"38356517.efc10a", "iType":"SendData", "descText":"", "payload":"[\n {\n \"uniqueId\": \"\",\n \"time\": \"2021-05-19T19:37:05.800Z\",\n \"data\": {\n \"temperature\": \"17\"\n }\n }\n]", "x":515, "y":200, "wires":[ [ "5139ef2a.05a86" ] ], "l":false }, { "id":"b7e2b58e.e39608", "type":"inject", "z":"38356517.efc10a", "name":"", "props":[ { "p":"payload" } ], "repeat":"", "crontab":"", "once":false, "onceDelay":0.1, "topic":"", "payload":"[{\"uniqueId\":\"neworleans\",\"data\":{\"temperature\":\"-1\"}}]", "payloadType":"json", "x":215, "y":200, "wires":[ [ "2196de99.bb4322" ] ], "l":false }, { "id":"48b4e7be.38da88", "type":"random", "z":"38356517.efc10a", "name":"random temperature", "low":"-10", "high":"65", "inte":"true", "property":"payload[0].data.Temperature", "x":275, "y":240, "wires":[ [ "2196de99.bb4322" ] ], "l":false }, { "id":"ebd0a829.213a98", "type":"inject", "z":"38356517.efc10a", "name":"", "props":[ { "p":"payload" } ], "repeat":"3600", "crontab":"", "once":true, "onceDelay":0.1, "topic":"", "payload":"[{\"uniqueId\":\"neworleans\",\"data\":{\"temperature\":\"0\",\"latitude\":52.765929,\"longitude\":6.27153}}]", "payloadType":"json", "x":215, "y":240, "wires":[ [ "48b4e7be.38da88" ] ], "l":false }, { "id":"aeba4e1c.0de5b", "type":"comment", "z":"38356517.efc10a", "name":"iotconnect.io \\n send", "info":"", "x":550, "y":140, "wires":[ ] }, { "id":"af31f3d5.073d", "type":"comment", "z":"38356517.efc10a", "name":"iotconnect.io \\n communicate", "info":"", "x":730, "y":140, "wires":[ ] }, { "id":"30c8ac63.5559d4", "type":"comment", "z":"38356517.efc10a", "name":"temperature", "info":"", "x":250, "y":160, "wires":[ ] }, { "id":"61178473.a0f70c", "type":"debug", "z":"38356517.efc10a", "name":"", "active":true, "tosidebar":true, "console":false, "tostatus":false, "complete":"true", "targetType":"full", "statusVal":"", "statusType":"auto", "x":955, "y":200, "wires":[ ], "icon":"font-awesome/fa-bug", "l":false }, { "id":"286ca1a4.4a214e", "type":"comment", "z":"38356517.efc10a", "name":"iotconnect.io \\n receive", "info":"", "x":970, "y":140, "wires":[ ] }, { "id":"c56a4d18.5d2ea", "type":"debug", "z":"38356517.efc10a", "name":"", "active":true, "tosidebar":true, "console":false, "tostatus":false, "complete":"true", "targetType":"full", "statusVal":"", "statusType":"auto", "x":775, "y":220, "wires":[ ], "icon":"font-awesome/fa-bug", "l":false }, { "id":"786abb8b.c1d6d4", "type":"IoTConnect-Injector", "z":"38356517.efc10a", "iType":"Disconnect", "descText":"", "payload":"", "x":250, "y":300, "wires":[ [ "5139ef2a.05a86" ] ] }, { "id":"7c8682e9.dc959c", "type":"IoTConnect-Injector", "z":"38356517.efc10a", "iType":"SendCommandAck", "descText":"", "payload":"", "x":775, "y":300, "wires":[ [ "5139ef2a.05a86" ] ], "l":false }, { "id":"e6dd8afb.945498", "type":"switch", "z":"38356517.efc10a", "name":"ACK?", "property":"payload.ack", "propertyType":"msg", "rules":[ { "t":"true" }, { "t":"false" } ], "checkall":"false", "repair":false, "outputs":2, "x":590, "y":340, "wires":[ [ "69158414.46415c" ], [ "9a7ee3c.75f052" ] ] }, { "id":"f2c5a2d1.4b0a", "type":"debug", "z":"38356517.efc10a", "name":"", "active":false, "tosidebar":true, "console":false, "tostatus":false, "complete":"\"ACK sent\"", "targetType":"jsonata", "statusVal":"", "statusType":"auto", "x":755, "y":340, "wires":[ ], "icon":"font-awesome/fa-bug", "l":false }, { "id":"9a7ee3c.75f052", "type":"debug", "z":"38356517.efc10a", "name":"", "active":false, "tosidebar":true, "console":false, "tostatus":false, "complete":"\"ACK not required\"", "targetType":"jsonata", "statusVal":"", "statusType":"auto", "x":755, "y":380, "wires":[ ], "icon":"font-awesome/fa-bug", "l":false }, { "id":"6965f124.a61b", "type":"comment", "z":"38356517.efc10a", "name":"receipt mgt", "info":"", "x":570, "y":381, "wires":[ ] }, { "id":"69158414.46415c", "type":"function", "z":"38356517.efc10a", "name":"", "func":"msg.payload['st']=6\nmsg.payload['msg']=\"ACK Success\"\nreturn msg;", "outputs":1, "noerr":0, "initialize":"", "finalize":"", "x":685, "y":321, "wires":[ [ "7c8682e9.dc959c", "f2c5a2d1.4b0a" ] ], "l":false } ]
Refer to this post for the correct ACK handling.
In parallel, another service at the subcontractor can use the IoTCloud REST API to provision the device on the cloud.
I may write a post on how to do that.
For the moment, I'm doing this manually in the portal:
It's also fast, but automation is better.
Test!
The subcontractor engages in testing. Manually or automatic.
Both Node-RED log and cloud portal report success.
In our case, this can also be scripted: start Node-RED and parse the output.
Or the IoTConnect REST API can be used to retrieve the status. This could again be done at the production line.
Successfully programmed devices can be packaged, failing ones binned in a rework container.
You can replicate this. With your Windows laptop, a Raspberry Pi, BB, ... Try it. |