The bulk certs module allows for the creation of large batches of X.509 certificates, and optionally register them with AWS IoT.
When a batch is requested, a task is created to track the creation of certificates. Upon task creation, the creation call is returned immediately to the caller. The task itself is then split in smaller chunks to allow for quick processing. Once all chunks are complete, the overall task is complete, and the certificates can be downloaded as a single zip file.
When creating device certificates they can be signed by the Amazon Root certificate authority (CA), or alternatively signed by other root CAs. It is recommended that other root CAs are used due to:
- currently all device certificates created using the Amazon Root CA are long-lived certificates. With other root CAs you have the opportunity to specify device expiration dates
- if you need devices to self-register themselves using just-in-time registration (JITR) or just-in-time provisioning (JITP) type provisioning flows, then you must register a root CA
- a recommended best practice is to use different CAs per device suppliers. Then in the event of a CA being compromised, just that single supplier can be notified and have a new CA issued for use with the other suppliers remaining unaffected
The following outlines the steps for registering other root CAs.
Note that a CA may be registered with just one account within a region. If your devices need the ability to connect to multiple accounts within a region, such as having the same device certificate signed by a single CA able to connect to different development, testing, and production accounts, then use the CDF provisioning module to auto-create the device certificate as part of the provisioning flow which supports multi-account registration.
If you need to create your own CA, refer to Create a CA certificate.
If you already have a CA, or have created one using the previous step, then refer to Register your CA certificate.
To sign the device certificates with a custom CA, the CA private key is required. This module uses AWS Key Management Service (KMS) to securely store and encrypt the CA private key in AWS Systems Manager. When you deploy this module, one of the required parameters is to specify whether an existing KMS key should be used or a new one created for this purpose.
The following AWS CLI command uploads the private key file to SSM. It is recommended to use the CLI rather than copy pasting into the console to avoid possible formatting issues in the console.
Note: By convention, this module expects the SSM parameter name in the format cdf-ca-key-<CA KEY ID>
, where the <CA KEY ID>
is the CA certificate ID (aka CA name in the AWS Console) when it is registered in AWS IoT.
# <KMS KEY ID>: The `KmsKeyId` parameter of the `cdf-bulkcerts-<env>` CloudFormation stack
# <CA KEY ID>: the CA certificate ID (aka CA name in the AWS Console)
# <PRIVATE KEY LOCATION>: the file path of the CA private key to store in SSM
aws ssm put-parameter --type SecureString /
--key-id <KMS KEY ID> /
--name cdf-ca-key-<CA KEY ID> /
--value file://<PRIVATE KEY LOCATION> --overwrite
Customers who need additional security can use Private CAs in AWS Certificate Manager (ACMPCA) to manage the generation of their device certificates. Additional resources can be found here.
if you need to create a private CA, refer to PCA Planning Documentation. Once you have created your private CA go to the next step and use its arn as value for the supplier.
As you will see from the walkthrough below, an alias (supplierId
) is required as part of the REST API call to create device certificates. Behind the scenes this alias is mapped to specific CAs to use to sign the device certificates. This mapping needs to be defined before device certificates can be created for a specific supplier.
The alias to CA ID is defined in the application configuration at time of deployment. If this mapping needs to change post deployment, then the application configuration should be updated followed by a redeployment. The following is an excerpt of a sample application configuration where aliases supplier1
and supplier2
are mapped to different custom CAs, supplier3
is mapped to the Amazon Root CA and supplier4
is mapped to the ACMPCA:
{
...
"supplierRootCa": {
"supplier1": "856058e172339c0112ede7ea58616e661946bf8a85490410f8131ce651417425",
"supplier2": "3d2ecfdb0eba2898626291e7e18a37cee791dbc81940a39e8ce922f9ff2feb32",
"supplier3": "AwsIotDefault",
"supplier4" : "arn:aws:acm-pca:us-west-2:xxxxxxxxxxxx:certificate-authority/17ef9add-91a6-4c1f-b13b-0f6ec1952722"
}
...
}
Refer to Application configuration for more details on what configuration may be specified.
POST /supplier/<supplierId>/certificates
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"quantity": 2
}
Content-Type: application/vnd.aws-cdf-v1.0+json
location: /certificates/jshs783h
x-taskid: jshs783h
{
"taskId": "jshs783h",
"status": "pending"
}
POST /supplier/<supplierId>/certificates
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"quantity": 2,
"certInfo": {
"country": "US"
}
}
Content-Type: application/vnd.aws-cdf-v1.0+json
location: /certificates/jshs783h
x-taskid: jshs783h
{
"taskId": "jshs783h",
"status": "pending"
}
1c/ Request a batch of certificates, auto-generating the certificate common name (incremental method)
The following example will create 100 sequential device certificates with a commonName
starting from `templateFoo::
`AB1CD79EF
and ending with `templateFoo::
`AB1CD79F54
. This commonName
format of `<proviioningTemplateName>::
`<deviceId>
is useful in JITR provisioning flows where devices are able to self register based on information presented in the certificate. Note that the count provided in the commonName
field ${incement(100)}
will override the quantity value
POST /supplier/<supplierId>/certificates
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"quantity": 100,
"certInfo": {
"commonName": "`templateFoo::`AB1CD79EF${incement(100)}",
"includeCA": true
}
}
Content-Type: application/vnd.aws-cdf-v1.0+json
location: /certificates/jshs783h
x-taskid: jshs783h
{
"taskId": "jshs783h",
"status": "pending"
}
Th following example will create 3 device certificates with commonNames
of `templateFoo::
`AB1CD79EF1
, `templateFoo::
`AB1CD79EF2
and `templateFoo::
`AB1CD79EF3
. Note that the number of elements in the commonNameList
array would override the quantity value
POST /supplier/<supplierId>/certificates
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"quantity": 3,
"certInfo": {
"commonName": "`templateFoo::`${list}" ,
"commonNameList":["AB1CD79EF1","AB1CD79EF2","AB1CD79EF3"]
}
}
Content-Type: application/vnd.aws-cdf-v1.0+json
location: /certificates/jshs783h
x-taskid: jshs783h
{
"taskId": "jshs783h",
"status": "pending"
}
The following example will create 100 device certificates with a static commonName
of `templateFoo::
`AB1CD79EF
.
POST /supplier/<supplierId>/certificates
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"quantity": 100,
"certInfo": {
"commonName": "`templateFoo::`AB1CD79EF${static}"
}
}
Content-Type: application/vnd.aws-cdf-v1.0+json
location: /certificates/jshs783h
x-taskid: jshs783h
{
"taskId": "jshs783h",
"status": "pending"
}
If the certificate batch creation task is pending
or in_progress
, a 303
redirect will occur to return the task status.
GET /certificates/<taskId>
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
303 Redirect to /certificates/<taskId>/task
Content-Type: application/vnd.aws-cdf-v1.0+json
{
"taskId": "jshs783h",
"status": "in_progress",
"chunksPending": 2,
"chunksTotal": 12
}
GET /certificates/<taskId>
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/zip
Content-Type: application/zip
<Binary file>
GET /certificates/<taskId>?downloadtype=signedUrl
Accept: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
Content-Type: application/vnd.aws-cdf-v1.0+json
[ "url1", "url2", ... ]