How do I set up JITP with AWS IoT Core?

Last updated: 2021-02-15

I want to set up a just-in-time provisioning (JITP) environment that has a custom root certificate authority (CA) registered with AWS IoT Core. How do I do that?

Short description

To set up a JITP environment with AWS IoT Core, first register your CA with AWS IoT Core, then attach a provisioning template to your CA.

Resolution

Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you’re using the most recent AWS CLI version.

Create a self-signed root CA and verification certificate

1.    If you haven't already, install OpenSSL.

2.    Run the following OpenSSL command to create a device root CA private key:

$ openssl genrsa -out deviceRootCA.key 2048

3.    Using the VIM text editor, create a custom OpenSSL.conf file by doing the following: Run the following VIM command to create a custom OpenSSL.conf file:

$ vi deviceRootCA_openssl.conf

Press i on the keyboard to edit the .conf file. Then, copy and paste the following into the file:

[ req ]
distinguished_name       = req_distinguished_name
extensions               = v3_ca
req_extensions           = v3_ca

[ v3_ca ]
basicConstraints         = CA:TRUE

[ req_distinguished_name ]
countryName              = Country Name (2 letter code)
countryName_default      = IN
countryName_min          = 2
countryName_max          = 2
organizationName         = Organization Name (eg, company)
organizationName_default = AMZ

Press esc on your keyboard, followed by :wq! to save the .conf file. Then, press Enter to exit the file.
Note: You can then run the following Linux command to confirm that the OpenSSL.conf file was created:

$ cat deviceRootCA_openssl.conf

4.    Run the following OpenSSL command to create a device root CA certificate signing request (CSR):

$ openssl req -new -sha256 -key deviceRootCA.key -nodes -out deviceRootCA.csr -config deviceRootCA_openssl.conf

5.    Run the following OpenSSL command to create a device root CA certificate:

$ openssl x509 -req -days 3650 -extfile deviceRootCA_openssl.conf -extensions v3_ca -in deviceRootCA.csr -signkey deviceRootCA.key -out deviceRootCA.pem

6.    Run the following AWS CLI command to get the registration code for the AWS Region that you want to use JITP in:

$ aws iot get-registration-code --region us-east-2

Note: Make sure that you replace us-east-2 with the Region that you want to use JITP in.

7.    Run the following OpenSSL command to create a verification key:

$ openssl genrsa -out verificationCert.key 2048

8.    Run the following OpenSSL command to create a verification certificate CSR:

$ openssl req -new -key verificationCert.key -out verificationCert.csr

Note: Enter the Registration Code in the Common Name field. For example: Common Name (server FQDN or YOUR name) []: xxxxxxxx8a33da. Leave the other fields blank.

9.    Run the following OpenSSL command to create the verification certificate:

$ openssl x509 -req -in verificationCert.csr -CA deviceRootCA.pem -CAkey deviceRootCA.key -CAcreateserial -out verificationCert.crt -days 500 -sha256

Note: The registration code of your root CA’s Region is required for the verification certificate to be certified by AWS IoT Core.

For more information, see Just-in-time provisioning.

Create a JITP template

1.    Create an AWS Identity and Access Management (IAM) role for your AWS IoT Core service and name it JITPRole. For detailed steps, see Create a logging role (steps one and two).
Note: You must include the IAM role’s Amazon Resource Name (ARN) in the following JITP template.

2.    Using the VIM text editor, create a JITP template JSON file by doing the following: Run the following VIM command to create a JITP template JSON file:

$ vi jitp_template.json

Important: Make sure that you save the template with the file name jitp_template.json. Press i on the keyboard to edit the JITP template. Then, copy and paste the following JITP template into the file:
Important: Replace the roleArn value with the IAM Role ARN for your AWS IoT Core service. Also, replace the <ACCOUNT_ID> value with your AWS account ID.

{
 "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" : {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyDocument\" : \"{ \\\"Version\\\": \\\"2012-10-17\\\", \\\"Statement\\\": [ { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Connect\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:us-east-2:<ACCOUNT_ID>:client\\\/${iot:Connection.Thing.ThingName}\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Publish\\\", \\\"iot:Receive\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:us-east-2:<ACCOUNT_ID>:topic\\\/${iot:Connection.Thing.ThingName}\\\/*\\\" ] }, { \\\"Effect\\\": \\\"Allow\\\", \\\"Action\\\": [ \\\"iot:Subscribe\\\" ], \\\"Resource\\\": [ \\\"arn:aws:iot:us-east-2:<ACCOUNT_ID>:topicfilter\\\/${iot:Connection.Thing.ThingName}\\\/*\\\" ] } ] }\" } } } }",
 "roleArn":"arn:aws:iam::<ACCOUNT_ID>:role/JITPRole"
}

Press esc on your keyboard, followed by :wq! to save the JITP template file.
Choose Enter to exit the file.

Note: The following IAM policies are included in the example JITP template:
AWSIoTLogging
AWSIoTRuleActions
AWSIoTThingsRegistration

You must be signed in to your AWS account to view the policy links. For more information, see Provisioning templates.

Register your self-signed root CA certificate with AWS IoT Core

Run the following register-ca-certificate command to register the device root CA as a CA certificate in AWS IoT Core:

Note: Make sure that you replace us-east-2 with the Region that you want to use JITP in.

$ aws iot register-ca-certificate --ca-certificate file://deviceRootCA.pem --verification-cert file://verificationCert.crt --set-as-active --allow-auto-registration --registration-config file://jitp_template.json --region us-east-2

Note: Adding the parameter --registration-config attaches the JITP template that you created to the CA certificate. The command response will return the ARN of the CA certificate.

For more information, see Register your CA certificate.

Create device certificates and perform JITP

Important: Make sure that you use the same directory where you created the original device root CA files.

1.    Download the RootCA1 and save it with the file name awsRootCA.pem.
Note: The RootCA1 is used for server-side authentication of publish requests to AWS IoT Core. For more information, see CA certificates for server authentication.

2.    Run the following OpenSSL command to create a device private key:

$ openssl genrsa -out deviceCert.key 2048

3.    Run the following OpenSSL command to create a device CSR:

$ openssl req -new -key deviceCert.key -out deviceCert.csr

Note: The example JITP template requires the ThingName value to equal the certificate’s CommonName value. The template also requires the CountryName value to equal the Country value in the CA certificate. For example:

Country Name (two-letter code) []:IN
Common Name (eg. server FQDN or YOUR name) []: DemoThing

The JITP template provided in this article also uses the AWS::IoT::Certificate::Country certificate parameter, which requires you to add a value. Other potential certificate parameters include:
AWS::IoT::Certificate::Country
AWS::IoT::Certificate::Organization
AWS::IoT::Certificate::OrganizationalUnit
AWS::IoT::Certificate::DistinguishedNameQualifier
AWS::IoT::Certificate::StateName
AWS::IoT::Certificate::CommonName
AWS::IoT::Certificate::SerialNumber
AWS::IoT::Certificate::Id

4.    Run the following OpenSSL command to create a device certificate:

$ openssl x509 -req -in deviceCert.csr -CA deviceRootCA.pem -CAkey deviceRootCA.key -CAcreateserial -out deviceCert.crt -days 365 -sha256

5.    Run the following command to combine the root CA certificate and device certificate:

$ cat deviceCert.crt deviceRootCA.pem > deviceCertAndCACert.crt

6.    Use Eclipse Mosquitto to make a dummy publish call to AWS IoT Core and initiate the JITP process. Make sure that you enter values for the following fields before you run the command: ClientID (enter dummy text) Topic Name (enter dummy text) AWS Root CA 1 Certificate Device private key rootCA + deviceCert AWS IoT -ats Endpoint Note: You can also use the AWS Device SDK to make Publish calls to AWS IoT Core.

Example Eclipse Mosquitto dummy publish call command

$ mosquitto_pub --cafile awsRootCA.pem --cert deviceCertAndCACert.crt --key deviceCert.key -h a27icbrpsxxx-ats.iot.us-east-2.amazonaws.com -p 8883 -q 1 -t  foo/bar -i  anyclientID --tls-version tlsv1.2 -m "Hello" -d

Important: Replace a27icbrpsxxx-ats.iot.us-east-2.amazonaws.com with your own endpoint before running the command. To confirm your own endpoint, open the the AWS IoT Core console. Then, choose Settings. Your endpoint is listed in the Custom endpoint pane.

Example response from the Eclipse Mosquitto dummy publish call command

Client anyclientID sending CONNECT  
  Error: The connection was lost. // The error is expected for the first connect call

Note: The dummy publish call fails the first time. When AWS IoT Core receives the dummy publish call, it creates a Certificate, Policy, and Thing. It also attaches the Policy to the Certificate, and then attaches the Certificate to the Thing. The next time you perform JITP, the IoT policy that was first created is the one that will be used. A new IoT policy will not be created.

7.    Do the following to confirm the creation of resources:
Open the AWS IoT Core console.
Choose Manage.
Choose Things.
Choose DemoThing.
Verify that the certificate was created and it’s in ACTIVE state.
Then, choose Policies and verify that the IAM policy is attached.

Use device certificates in general operation

Note: The value of Client ID that is added in the publish command must be the same as the ThingName that was created during the JITP process. The Topic Name added to the publish command must also follow the format ThingName/*. In the next publish call, you can use the deviceCert.crt instead of deviceCertAndCACert.crt.

1.    Open the AWS IoT Core console.

2.    Choose Test.

3.    For Subscription Topic, enter DemoThing/test.

4.    Run the following Eclipse Mosquitto publish call command to AWS IoT Core:

$ mosquitto_pub --cafile awsRootCA.pem --cert deviceCert.crt --key deviceCert.key -h a27icbrpsxxx-ats.iot.us-east-2.amazonaws.com -p 8883 -q 1 -t  DemoThing/test -i  DemoThing --tls-version tlsv1.2 -m "Hello" -d

Important: Replace a27icbrpsxxx-ats.iot.us-east-2.amazonaws.com with your own endpoint before running the command. To confirm your own endpoint, open the AWS IoT Core console. Then, choose Settings. Your endpoint appears in the Custom endpoint pane. Also, make sure that you use the custom device certificates that were generated by your custom root CA.

After running the command, you will see that the message is received on the IoT Core Test console.

Create additional device certificates

To create more device certificates and register them to AWS IoT Core, repeat the steps outlined in the Create device certificates and perform JITP section.

Other JITP templates

To fetch the ThingName value from the CommonName field of the certificate and to provide admin permissions in the policy, use the following JITP template:

{
 "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" : {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyDocument\" : \"{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Action\\\":\\\"iot:*\\\",\\\"Resource\\\":\\\"*\\\"}]}\" } } } }",
 "roleArn":"arn:aws:iam::<ACCOUNT_ID>:role/JITPRole"
}

To fetch the ThingName value from the CommonName field of the certificate and provide a predefined policy name, use the following JITP template:

{
 "templateBody":"{ \"Parameters\" : { \"AWS::IoT::Certificate::CommonName\" : { \"Type\" : \"String\" },\"AWS::IoT::Certificate::Country\" : { \"Type\" : \"String\" }, \"AWS::IoT::Certificate::Id\" : { \"Type\" : \"String\" }}, \"Resources\" : { \"thing\" : { \"Type\" : \"AWS::IoT::Thing\", \"Properties\" : { \"ThingName\" : {\"Ref\" : \"AWS::IoT::Certificate::CommonName\"}, \"AttributePayload\" : { \"version\" : \"v1\", \"country\" : {\"Ref\" : \"AWS::IoT::Certificate::Country\"}} } }, \"certificate\" : { \"Type\" : \"AWS::IoT::Certificate\", \"Properties\" : { \"CertificateId\": {\"Ref\" : \"AWS::IoT::Certificate::Id\"}, \"Status\" : \"ACTIVE\" } }, \"policy\" :  {\"Type\" : \"AWS::IoT::Policy\", \"Properties\" : { \"PolicyName\" :  \"Policy_Name\"} } } }",
 "roleArn":"arn:aws:iam::<ACCOUNT_ID>:role/JITPRole"
}

Important: Replace Policy_Name with the policy name of your choice.


Did this article help?


Do you need billing or technical support?