Introduction

Security of operations and security of data are among the top priorities of customers dealing with sensitive information or operating in highly regulated markets. Internet of Things (IoT) customers have the additional challenge of enabling high security standards for IoT communications to their cloud platforms. Data encryption with asymmetric algorithms and cryptographic keys are widely adopted mechanisms to secure communication traffic.

The most important requirement related to cryptographic keys for IoT devices is that they must be securely stored to deny direct access from malicious users. The leakage of a private key would allow a malicious user to impersonate an IoT device and access sensitive resources from corporate IT, or send counterfeit data, with threatening results.

Hardware Security Modules (HSMs) provide an effective hardware-based mechanism to avoid exposure of private keys and are a trending technology among customers that want to protect their digital keys and certificates. However, their integration in the software stack of IoT Devices is not a trivial process.

This post will walk you through some notable examples of security challenges across IoT Industry verticals, and provide a sample implementation of a secure communication IoT client with few lines of code on AWS using a simulated Hardware Security Module, AWS IoT Device SDK v2, and AWS IoT.

Cybersecurity in IoT: protecting your device secrets

A common standard in IoT communications security is to leverage a Transport Level Security layer (TLS) to protect data as it moves between the device and the cloud. For example AWS IoT Core adopts TLS v1.2 and X.509 certificates for its IoT transport level security.

In a secure communication scenario, IoT devices are provided with a key-pair, containing the private key and the public certificate, and a trust-store containing the server Certificate Authority (CA) chain. This information is needed to allow both server authentication on the client side (using the trust-store) and the client authentication on the server side (using the key pair). This approach is called Mutual TLS authentication and is depicted in the following diagram.

Figure 1: High level functional view of mutual authentication between IoT devices and AWS IoT Core

Customers managing the IoT device software and operations are ultimately responsible for provisioning, storing, and utilizing such secrets, and limiting the exposure of the keys. For additional information on how AWS and customers manage workload security, refer to the Shared Responsibility Model.

Risks of IoT device secrets leakage

Examples of risks related to IoT device secrets leaking include the following:

Automotive telematics IoT customers are exposed to the risk that a malicious attacker can impersonate a vehicle and send counterfeit data to the cloud, or download sensitive data from the corporate datacenter.
In March 2021, The United Nations Economic Commission for Europe (UNECE) approved and published UN Regulations on CyberSecurity (UN R155) and SW updates (UN R156) for connected vehicles. In the regulations, security controls for the storage of cryptographic keys is among the proposed mitigation actions for cyberthreats.
IoT smart cities customers are exposed to the risk that operations of their connected smart lighting poles, traffic lights, etc. could be tampered with, resulting in service outages or public safety issues.
IoT smart grids customers are concerned about fraudulent users tampering with a connected smart meter and providing counterfeit data to the central grid system.

But how can device secrets be compromised?

In a typical lifecycle, security keys for an IoT device must be provisioned, stored, regularly rotated, and used for cryptographic operations.

Lifecycle state
Security threat
Mitigation

Provisioning
Injecting the keys from outside of the device results in risk of exposure to a malicious attacker.
Generate private keys in the device, and never expose outside.

Storage
A malicious attacker can tamper the device and/or take control of the device software to access its non-volatile memory.
Use a device with anti-tampering mechanisms (such as HSM). If not available, encrypting your storage can reduce risk.

Usage
If a software cryptographic library is used, the software will need to access the secrets through volatile memory (without encryption). An attacker could retrieve it through JTAG (Joint Test Action Group) debuggers or similar mechanisms.
Perform cryptographic operations off-chip so the keys are not exposed in the software flow.

In general, storing device keys inside the device memory or in cloud storage is not advisable. In a security incident known as the SolarWinds hack in 2020, malicious attackers were able to gain access to the protected data in the Active Directory Federation Services (ADFS) of the customer, collect encryption keys that were stored in the system, and use them to impersonate legitimate users, stealing sensitive data.

How Hardware Security Modules improve the security of IoT communications

A Hardware Security Module (HSM) is a physical module in the form of a cryptographic chip. It can be soldered on board of the device, or connected to a high speed bus. It provides the following:

A secure key vault store and entropy-based random key generation.
Implements cryptographic operations on-chip, without exposing them to the software stack.
Advanced anti-tampering mechanisms for physical protection of the chip’s non volatile memory (NVM).

Figure 2: High level functional view of Hardware Security Module integration with business applications using PKCS#11 APIs

Using such a device, customers can reduce the exposure of security keys, since the private key generation and cryptographic operations happen in the chip itself.
Applications interact with the device through specific purpose-built APIs, of which one of the industry standards is PCKS#11 or “Cryptoki”. A full description of the PKCS#11 APIs can be consulted at OASIS PKCS#11 specifications.

Using an HSM mitigates the risks of exposure of private keys as follows:

Lifecycle state
Security threat

Provisioning
Injecting the keys from outside of the device results in risk of exposure to a malicious attacker.

Storage
A malicious attacker can tamper the device and/or take control of the device software to access its non-volatile memory.

Usage
If a software cryptographic library is used, the software will need to access the secrets through volatile memory (without encryption). An attacker could retrieve it through JTAG (Joint Test Action Group) debuggers or similar mechanisms.

Due to the complexity of the implementation, interfacing an HSM through PCKS#11 can be a time consuming and difficult task for an IoT engineer.

AWS IoT Device SDKs v2 helps customers that want to implement HSM-based device security, providing dedicated libraries to help developers implement a secure MQTT client connection using an HSM with PKCS#11 with few lines of code.

Implementing a secure MQTT client from your IoT device with IoT Device SDK for Python v2 and software HSM

In this blog, you will replicate the steps required to set up an MQTT IoT Client to AWS IoT Core using a Hardware Security Module and AWS IoT Device SDK for Python v2 in few lines of code.

The following diagram depicts the architecture of the proposed solution:

Figure 3: High level architecture diagram of the proposed solution for secure IoT communications between an IoT device and AWS IoT Core

The proposed example simulates a Hardware Security Module with a software-HSM using softHSM2 from the OpenDNSSEC project.

The device certificate will be signed by the Amazon Root CA built-in to AWS IoT Core. In case a Private CA is used, the request must be sent to the proper Public Key Infrastructure of the customer (PKI).

Prerequisites

An AWS account.
A Linux debian-based machine.
OpenSSL 3.0+ installed on your machine (refer to this link for instructions).
Python 3.6+ installed on your machine (refer to this this link for instructions).
softHSM version2 installed on your machine (refer to this this link for instructions).
Amazon RSA 2048 bit rootCA 1 key (download here).

Step 1: Provisioning a device certificate through HSM in AWS IoT Core

A device certificate for your device must be provisioned and activated in AWS IoT Core. You can use the softHSM2 software to simulate a HSM, and openssl to generate a Certificate Signing Request (CSR).

It is not scope of this blog to provide examples of how to use softHSM2, and openssl; please refer to the software documentation.

The steps required are the following:

Install softHSM2 and pcks11 tools (refer to this link for instructions):

sudo apt install softhsm
sudo apt install opensc opensc-pkcs11 openssl libengine-pkcs11-openssl

Configure a token and a generate a private key:

sudo softhsm2-util –init-token –free –label <token-label>

This command should prompt you to enter a PIN, just follow instructions and remember that PIN.

Get the Slot ID:

Once you have your token configured, you need to retrieve the Slot number to be used in the next command. To do that, run the following:

sudo softhsm2-util –show-slots

You should get a response like the following:

Available slots:
Slot <slot-id>
Slot info:
Description: SoftHSM slot ID 0x35927c85
Manufacturer ID: SoftHSM project
Hardware version: 2.6
Firmware version: 2.6
Token present: yes
Token info:
Manufacturer ID: SoftHSM project
Model: SoftHSM v2
Hardware version: 2.6
Firmware version: 2.6
Serial number: aede8dd735927c85
Initialized: yes
User PIN init.: yes
Label: <token-label>
Slot 1
Slot info:
Description: SoftHSM slot ID 0x1
Manufacturer ID: SoftHSM project
Hardware version: 2.6
Firmware version: 2.6
Token present: yes
Token info:
Manufacturer ID: SoftHSM project
Model: SoftHSM v2
Hardware version: 2.6
Firmware version: 2.6
Serial number:
Initialized: no
User PIN init.: no
Label:

Check which slot contains the label related to the previous command and get its ID (the one on the left of the Slot <slot-id> line).

Generate the certificate:

Once you have generated the token on softHSM2 and you have the <slot-id>, you can use pkcs11-tool to generate a key-pair in it. To do that, use the following command replacing the <PIN> and <token-lable> placeholders.

Note: the module path /usr/lib/softhsm/libsofthsm2.so is based on the ubuntu-like instance of the machine. It may vary according to the environment you are testing into.

sudo pkcs11-tool -l –pin <PIN> –keypairgen –hash-algorithm “SHA256” –key-type RSA:2048 –label <token-label> –slot <slot-id> –module /usr/lib/softhsm/libsofthsm2.so

You should get a response like this:

Key pair generated:
Private Key Object; RSA
label: <token-label>
Usage: decrypt, sign, unwrap
Access: sensitive, always sensitive, never extractable, local
Public Key Object; RSA 2048 bits
label: <token-label>
Usage: encrypt, verify, wrap
Access: local

Create a Certificate Signing Request using openssl and softHSM2. (see following link for a sample walkthrough).
You can generate an RSA key with a length of 2048 bits and SHA256 algorithm. Refer to AWS IoT Core documentation for a full list of certificate signing algorithms supported:

sudo openssl req -new -engine pkcs11 -keyform engine -key “pkcs11:object=<token-label>;pin-value=<PIN>” -out certificate.csr

This command will prompt you to enter certificate information to be embedded into the sign request.

Use the following command to check if the Certificate Signing Request has been filled with the correct information:

openssl req -in certificate.csr -noout -text

Issue the following AWS CLI command to generate a device certificate using your CSR:

aws iot create-certificate-from-csr
–set-as-active
–certificate-signing-request=file://certificate.csr

The command will provide you the Amazon Resource Name (ARN) of the certificate and the content:

{
“certificateArn”: “arn:aws:iot:{region}:{accountId}:cert/{certificateId}”,
“certificateId”: “{certificateId}”,
“certificatePem”: “<certificate-text>”
}

Store your public certificate in your device in a file called deviceCertificate.pem using the following AWS CLI command:

aws iot describe-certificate
–certificate-id “{certificateId}”| jq -r “.certificateDescription.certificatePem” > deviceCertificate.pem

Create a file called policy.json representing an IoT Policy with ‘Connect’ only action permissions:

{
“Version”: “2012-10-17”,
“Statement”: [
{
“Effect”: “Allow”,
“Action”: “iot:Connect”,
“Resource”: “arn:aws:iot:{region}:{accountId}:client/${iot:Connection.Thing.ThingName}”
}
]
}

Remember to replace {region} and {accountId} with your data.

Create the IoT policy resource in AWS IoT Core with the following AWS CLI command:

aws iot create-policy
–policy-name HSMDevicePolicy
–policy-document file://policy.json

Create a ‘thing’ in AWS IoT Core, attach the IoT policy to the certificate and the certificate to the ‘thing’ (remember to replace {certificateArn} with the value obtained from earlier):

aws iot create-thing
–thing-name HSMDevice

aws iot attach-policy
–target “{certificateArn}”
–policy-name “HSMDevicePolicy”
aws iot attach-thing-principal
–thing-name HSMDevice
–principal “{certificateArn}”

Step 2: Setting up the client with AWS IoT Device SDK2

With AWS IoT Device SDK2, you can create an IoT client and establish an MQTT connection using an HSM with a single line of code.

The following example uses Python as language of preference, but the feature is available for all the target languages of AWS IoT Device SDK 2 (see complete list at following link).

Prerequisites

PKCS#11 libso file is available for your HSM (in case of softHSM this is typically found in /usr/lib/softhsm/libsofthsm2.so). The file is required from the AWS IoT Device SDK 2 to properly set up the connection.
The following Python module (and its dependencies) must be installed before running the client: sudo pip3 install awsiotsdk.
Create a file called client.py with the following content:

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.

from awscrt import io
from awsiot import mqtt_connection_builder
from uuid import uuid4
import argparse
import sys

# Callback when connection is accidentally lost.
def on_connection_interrupted(connection, error, **kwargs):
print(“Connection interrupted. error: {}”.format(error))

# Callback when an interrupted connection is re-established.
def on_connection_resumed(connection, return_code, session_present, **kwargs):
print(“Connection resumed. return_code: {} session_present: {}”.format(return_code, session_present))

if __name__ == ‘__main__’:
# Create a connection using websockets.
# Note: The data for the connection is gotten from cmdUtils.
# (see build_pkcs11_mqtt_connection for implementation)

parser = argparse.ArgumentParser(description=’Input arguments for PKCS#11 MQTT Client.’)
parser.add_argument(“–pcks11lib”, type=str , help=”path to pkcs11 library”)
parser.add_argument(“–slot”, type=int , help=”HSM slot”)
parser.add_argument(“–pin”, type=str , help=”HSM slot pin”)
parser.add_argument(“–tokenlabel”, type=str , help=”HSM token label”)
parser.add_argument(“–keylabel”, type=str , help=”HSM ptivate key label”)
parser.add_argument(“–certpath”, type=str , help=”device certificate file path”)
parser.add_argument(“–endpoint”, type=str , help=”AWS IoT Core endpoint”)
parser.add_argument(“–cafile”, type=str , help=”CA certificate file path”)
parser.add_argument(“–clientid”, type=str , help=”clientId to use for MQTT connection”)

if len(sys.argv)==1:
parser.print_help(sys.stderr)
sys.exit(1)
args = parser.parse_args()

# We load the HSM library
pkcs11_lib_path = args.pcks11lib
print(f”Loading PKCS#11 library ‘{pkcs11_lib_path}’ …”)
pkcs11_lib = io.Pkcs11Lib(
file=pkcs11_lib_path,
behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT)
print(“Loaded!”)

pkcs11_slot_id = args.slot
pkcs11_pin = args.pin
pkcs11_tokenlabel = args.tokenlabel
pkcs11_keylabel = args.keylabel
certpath = args.certpath
endpoint = args.endpoint
cafile = args.cafile
clientid = args.clientid

# This is the core section of the example client.
# This single instruction instantiates an MQTT connection
# and performs encyrption operations using your HSM
# through the mqtt_connection_builder.mtls_with_pkcs11 method
mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11(
pkcs11_lib = pkcs11_lib,
user_pin = pkcs11_pin,
slot_id = pkcs11_slot_id,
token_label = pkcs11_tokenlabel,
private_key_label = pkcs11_keylabel,
cert_filepath = certpath,
endpoint = endpoint,
port = 8883,
ca_filepath = cafile,
on_connection_interrupted = on_connection_interrupted,
on_connection_resumed = on_connection_resumed,
client_id = clientid,
clean_session = False,
keep_alive_secs = 30)

connect_future = mqtt_connection.connect()

# Future.result() waits until a result is available
connect_future.result()
print(“Connected!”)

# Disconnect
print(“Disconnecting…”)
disconnect_future = mqtt_connection.disconnect()
disconnect_future.result()
print(“Disconnected!”)

The core of the above client implementation is the awsiot.mqtt_connection_builder.mtls_with_pkcs11() method from the AWS IoT Device SDK v2. The method is responsible for establishing a secure MQTT connection using the HSM to perform cryptographic operations.

Input parameters of the method are:

Parameter
Description

pkcs11_lib
Link to pkcs11 library (in case of softHSM this is typically found in /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so)

user_pin
User PIN of your HSM

slot_id
The slot id of your private key

token_label
The token label of your private key

private_key_label
The label of your private key

cert_filepath
The path to your deviceCertificate.pem file

ca_filepath
The path to your AmazonRootCA1.pem file

client_id
The clientID you want to use to connect AWS IoT Core

port
The port to use for MQTT connection

In our example, such parameters are provided through command line from the script arguments.

Step 3: Test your secure connection

You can retrieve your MQTT connection endpoint with the following CLI Command:

aws iot describe-endpoint
–endpoint-type iot:Data-ATS

Place the content of the endpointAddress field into the environmental variable to be used later:

export IOT_CORE_ENDPOINT=<endpointAddress>

Execute the above script using Python 3 executable (python3) with sudo privileges, in order to allow access to the HSM library:

sudo python3 client.py –pcks11lib /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
–pin <PIN>
–tokenlabel <token-label>
–certpath deviceCertificate.pem
–endpoint ${IOT_CORE_ENDPOINT}
–cafile AmazonRootCA1.pem
–clientid HSMDevice

A successful execution should return the following:

Loading PKCS#11 library ‘/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so’ …
Loaded!
Connected!
Disconnecting…
Disconnected!

Cleaning up resources

Detach ‘thing’ and IoT policy from registered certificate:

aws iot detach-policy
–target “{certificateArn}”
–policy-name “HSMDevicePolicy”
aws iot detach-thing-principal
–thing-name HSMDevice
–principal “{certificateArn}”

Delete device certificate, ‘thing,’ and IoT policy:

aws iot delete-thing
–thing-name HSMDevice
aws iot delete-certificate
–certificate-id “{certificateId}”
aws iot delete-policy –policy-name “HSMDevicePolicy”

Conclusion

In this post, we discussed some of the challenges faced in key / secret management of IoT device fleet and how they can be handled adopting Hardware Security Modules. We covered how the AWS IoT Device SDK2 can be used to create a simple MQTT client using a cryptographic chip in few lines of code.

By adopting these suggestions, customers can better avoid the exposure of their security keys to malicious users, increasing the overall security posture of their device fleets.

To learn more about how to use AWS IoT Core, you can refer to the documentation.

About the Authors

Daniele Crestini

Daniele is a IoT Data Consultant with AWS Professional Services. He helps AWS Customers achieve their business goals by architecting and building innovative solutions that leverage AWS IoT services on the AWS Cloud.

Iacopo Palazzi

Iacopo is an IoT Engineer working in the AWS Professional Services team based in Milan. He is also passionate about Software Development and DevOps, using them to implement robust, scalable and innovative architectures for AWS Customers.

Leave a Reply