How can I use JITP with AWS IoT Core?

Last updated: 2022-03-21

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 set up JITP with AWS IoT Core?

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 done so, install OpenSSL.

2.    Create a device root CA private key by running the following OpenSSL command:

$ openssl genrsa -out deviceRootCA.key 2048

3.    Using the VIM text editor, create a custom OpenSSL.conf file. To create and edit a custom OpenSSL.conf file, do the following:
Create a custom OpenSSL.conf file by running the following VIM command:

$ 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: To confirm that the OpenSSL.conf file was created, you can run the following Linux command:

$ cat deviceRootCA_openssl.conf

4.    Create a device root CA certificate signing request (CSR) by running the following OpenSSL command:

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

5.    Create a device root CA certificate by running the following OpenSSL command:

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

6.    Retrieve the registration code for the AWS Region that you want to use JITP in by running the following AWS CLI command:

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

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

7.    Create a verification key by running the following OpenSSL command:

$ openssl genrsa -out verificationCert.key 2048

8.    Create a verification certificate CSR by running the following OpenSSL command:

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

Then, 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.    Create the verification certificate by running the following OpenSSL command:

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

Important: 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 instructions, see Create a logging role (steps one and two).

Important: 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:
Create a JITP template JSON file by running the following VIM command:

$ 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:

{
 "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"
}

Important: Replace the roleArn value with the IAM Role ARN for your AWS IoT Core service. Replace the <ACCOUNT_ID> value with your AWS account ID. Replace us-east-2 with the AWS Region that you're using. 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:

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

Register the device root CA as a CA certificate in AWS IoT Core by running the following register-ca-certificate AWS CLI command:

Important: 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 returns the CA certificate's ARN.

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.    Create a device private key by running the following OpenSSL command:

$ openssl genrsa -out deviceCert.key 2048

3.    Create a device CSR by running the following OpenSSL command:

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

Important: 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.    Create a device certificate by running the following OpenSSL command:

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

5.    Combine the root CA certificate and device certificate by running the following command:

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

6.    Use Eclipse Mosquitto to make a test publish call to AWS IoT Core and initiate the JITP process.

Note: You can also use the AWS Device SDK to make Publish calls to AWS IoT Core.

Example Eclipse Mosquitto test publish call command

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 is listed in the Custom endpoint pane.

$ 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

Example response from the Eclipse Mosquitto test publish call command

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

Note: The test publish call fails the first time. When AWS IoT Core receives the test 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 is used. A new IoT policy isn't be created.

7.    Confirm the required resources were created by doing the following: 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:

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.

$ 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

After running the command, you will see that the message is received on the AWS 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?