Recently, I've been reseaching a lot about AWS IOT as part of few tasks I've been given at work. AWS IOT gives you the ability to communicate with your remote devices using a publish/subscribe protocol called MQTT, which uses certificates to identify the remote devices communicating with the IOT service.
As part of the research I've done, I've had the need to generate an AWS IOT signed certificate using a Certificate Signing Request (CSR), that will be used to identify the device I'm using for communication with the AWS IOT service.
Certificate Signing Request
A certificate signing request (CSR) is a file with encoded information used to identify the server it is created on during the process of applying for a TLS/SSL certificate from a certificate authority (CA).
This certificate will hold information such as the Domain Name of the server, it's Organization Name, Country, etc...
Example of a CSR file:
-----BEGIN NEW CERTIFICATE REQUEST-----MIIDVDCCAr0CAQAweTEeMBwGA1UEAxMV4uY29tMQ8wdt3mE2CiHYZnIAMgNVBAcMCEJyb2MA4GA1UdDwEB2d......NF7oU/xz0YCKOy9Zc4wddPUxETAPBgNVBAcMCzSwRfZSZTusPFTLKaqValdnS9Uw+6Vq7/I4ouDA8Q+8HQ==-----END NEW CERTIFICATE REQUEST-----
Creating CSR using openSSL
Let's create a CSR using the openSSL toolkit, we have to make sure that it is at least 2048-bit RSA encoded, and it has our relevant information.
openssl req -newkey rsa:2048 -nodes -keyout private.key -out cert.csr -subj "/C=US/ST=Client/L=Brooklyn/O=IRYL/CN=example.com`
Breakdown:
- req, tell openSSL to generate a CSR.
- -keyout, outputs the private key.
- -out, outputs the CSR, it acts as our public key.
- -subj, it fills the CSR information programmatically.
- -nodes, the private key should not be encrypted with a passphrase (you can decide otherwise, but Iāve not tested the connection to IOT with a passphrase).
We have now created a public key (cert.csr) and a private key (private.key) for our CSR.
Generate IOT Certificate
When applying for a TLS certificate signed by Amazon Certificate Authority we send the Certificate Authority our CSR as a public key, and in return we receive the signed TLS certificate.
Note: The Certificate Authority should only use the public key, and the private key should be saved and kept secret.
Because we have created an RSA key pair of public and private keys, you cannot decrypt the messages without the private key.
To create an IOT certificate, certified by AWS CA, we will have to pass our previously generated public key file ācert.csrā contents as a string to the following command:
aws iot create-certificate-from-csr --set-as-active --certificate-signing-request "cert.csr file contents as a string"
Iāve passed the certificate as a string programmatically, the code is found at the bottom.
Upon success, you will receive the following response:
{"certificateArn": "arn:aws:iot:....","certificateId": "....","certificatePem": "...."}
The response holds all the relevant details for the created certificate.
You then need to save the created IOT certificate as a file, Iāve called it āiot-cert.pemā, it will be used to connect to AWS IOT through MQTT requests.
You can save thecertificateId andcertificateArn as well, in another file.
Python Boto3 Example
The following code does all of the above mentioned steps:
- Create a CSR using openSSL toolkit.
- Apply for an AWS IOT certificate.
- Save that certificate information locally, to be used for communication using MQTT.
import subprocessimport jsonimport boto3# Definitionsiot = boto3.client("iot")certificateInfo = '/C=US/ST=Client Two/L=Brooklyn/O=IRYL/CN=examplebrooklyn.com'def generate_csr(certificateInfo):# -nodes - private key should not be encrypted with a passphrasesubprocess.run(["openssl", "req","-newkey","rsa:2048","-nodes","-keyout", "private.key","-out","cert.csr","-subj", certificateInfo])def read_csr(fileName):certificateString = ''# read certificate file to certificateStringwith open(fileName, 'r') as file:certificateString = file.read().replace('\n', '')return certificateStringdef generate_iot_certificate(certificateString):return iot.create_certificate_from_csr(certificateSigningRequest=certificateString, setAsActive=True)# 1. Saves iot certificate to file# 2. Save iot certificate arn and id to "latest-cert-info.txt"def handle_iot_certificate_generation_response(response):response_json = json.loads(response)certificateId = response_json["certificateId"]certificateArn = response_json["certificateArn"]certificatePem = response_json["certificatePem"]# Save iot certificate for connectionf = open("iot-cert.pem", "w")f.write(certificatePem)f.close()print("@@@ Saved IOT certificate for connection as 'iot-cert.pem'")# Save iot certificate Id + Arnf = open("latest-cert-info.txt", "w")f.write("Certificate Id: " + certificateId + "\n")f.write("Certificate Arn: " + certificateArn + "\n")f.close()print("@@@ Saved IOT certificate generation response to 'latest-cert-info.txt'")# Usagegenerate_csr(certificateInfo)# read created csr to stringcertificateString = read_csr("cert.csr")# create iot certificateresponse = generate_iot_certificate(certificateString)# save certificate information, create connection iot-cert.pemhandle_iot_certificate_generation_response(response)
Send a Message With MQTT
The MQTT protocol, is an extremely light-weight publish/subscribe protocol designed for messaging with remote devices and minimal network bandwidth usage.
AWS IOT uses it for its messaging transactions viaAWSIOTPythonSDK.
I've not used boto3 here because it relies on the permissions of the AWS account you have configured locally. We need to rely on our generated IOT certificate for communication.
https://docs.aws.amazon.com/iot/latest/developerguide/server-authentication.html
Go to:
- CA certificates for server authentication
- RSA 2048 bit key
- Amazon Root CA 1
- Save it as AmazonRootCA1.pem.
The following code uses the created TLS certificate, private key and root CA of Amazon to send the messages.
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClientimport timeimport json# Fill the following details correctly:host = '<iot-unique-id>.iot.us-west-2.amazonaws.com'rootCAPath = 'AmazonRootCA1.pem'certificatePath = 'iot-cert.pem'privateKeyPath = 'private.key'port = 8883clientId = 'your_client_id'topic = 'your_topic'# Init AWSIoTMQTTClientmyAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)myAWSIoTMQTTClient.configureEndpoint(host, port)myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)# AWSIoTMQTTClient connection configurationmyAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueingmyAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 HzmyAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 secmyAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec# Connect and subscribe to AWS IoTmyAWSIoTMQTTClient.connect()# Send 20 messagesloopCount = 0while loopCount < 20:messageJson = json.dumps({"message": "{} - Client: {}".format(loopCount, clientId)})myAWSIoTMQTTClient.publish(topic, messageJson, 1)loopCount += 1time.sleep(1)
That's it, your devices can now communicate with AWS IOT through a certificate generated programmatically.