Certificates in IoT Core
AWS IoT Core requires TLS mutual authentication and that relies on certificates. This means that even the simple use-case of connecting a device via MQTT requires several certificates, some visible, some less so.
In this article, we’ll take a look into the two ends of the TLS connection and the roles of the different certificates.
Let’s start with the direction that is easier to see: how the device authenticates itself to the cloud.
The device has a private key and an associated certificate. Usually, the key material is inside a secure element to prevent cloning and the certificate is provided by the manufacturer. Physical security is an important topic as devices are likely to be used in unsecure environments.
With the private key available to the device, the next step is to make AWS trust it. This is done by uploading its certificate to IoT Core.
With the certificate uploaded to AWS and the private key accessible to the device the client-side is authenticated during the MQTT connection. This forms the basis of device security.
In the basic case we discussed above the certificates are disconnected from each other, they have nothing in common. This is OK when all the devices are more or less the same, such as when you have only one customer and you control the provisioning (uploading the certificate) of new devices.
A more complex setup involves a CA certificate that signs all the device certificates. The ones embedded in the devices are still separate, so their private keys are only known to the device with the secure element. But when they connect to the cloud, they can present the signature from the CA certificate.
This setup allows 2 extra use-cases: JIT provisioning and separating permissions on the certificate level.
Before a device can connect to IoT Core, its certificate needs to be added to the AWS account. This is good when you have a limited number of devices and you control the provisioning process.
With a common CA certificate signing all device certificates you can also use just-in-time (JIT) provisioning. In this case, you only add the CA certificate and when the devices first connect AWS registers their certificate automatically. The trust anchor is the CA certificate which means this is a secure process even though you don’t know all the devices that will connect in advance.
This setup is a bit tricky to set up as AWS needs to verify that you have access to the CA’s private key, which usually you don’t. That requires collaboration with the manufacturer for each account you’ll want to enable JIT. Because of this, there are manual steps to set everything up.
Another use-case for a common CA certificate is to use issuer attribute values in the certificate policies. This makes it easier to use a single policy and still provide fine-tuned permissions to different groups of devices.
This can be useful, for example, when you have multiple customers each with their own CA certificate. Instead of writing a policy for each of them and then associate the correct one to their devices, you can use a single policy and define permissions using conditions.
While connecting a device is mostly about how to make AWS accept the certificate, TLS mutual auth has another end: the device also needs to verify the cloud endpoint. The basic principle is the same: AWS presents a certificate and the device decides whether to trust it or not.
AWS provides a CA certificate as the root of trust. This is called “Amazon Root CA” and you can download it from the documentation page.
The device needs to embed one of these root certificates for validation.
Trust anchor lists
Why this complication? After all, how is it different than opening to the amazon.com website in a browser? You don’t need to download any root certificates for that, yet the connection is still verified.
The browser already contains a list of root CAs it trusts. For example, Chromium has a root store with the AWS certificates included.
Some other browsers and other software rely on the platform trust anchor list:
$ trust list | grep Amazon label: Amazon Root CA 1 label: Amazon Root CA 2 label: Amazon Root CA 3 label: Amazon Root CA 4
Why it’s needed for an IoT device is because it does not have this list of trusted CAs. By embedding the AWS Root CA you add a trust anchor so that certificates signed by it will be trusted.
Under the hood
What happens when the device connects to the MQTT endpoint is that AWS returns a certificate chain that goes up to the trusted certificate. For example, CA 3 uses ECC, so AWS returns a chain going up to that one.
To see the certificates returned by connecting to the endpoint in your account, use this script:
openssl s_client \ -connect $(aws iot describe-endpoint --endpoint-type iot:Data-ATS --query 'endpointAddress' --output text):8883 \ -showcerts \ -cipher aECDSA
This returns a lot of things, with 2 certificates included:
... Certificate chain 0 s:C = US, ST = Washington, L = Seattle, O = Amazon.com Inc., CN = *.iot.eu-central-1.amazonaws.com i:C = US, O = Amazon, OU = Server CA 3B, CN = Amazon -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- 1 s:C = US, O = Amazon, OU = Server CA 3B, CN = Amazon i:C = US, O = Amazon, CN = Amazon Root CA 3 -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- ...
The first one is for the domain itself. Decoded, it looks like this:
Common Name: *.iot.eu-central-1.amazonaws.com Issued By: Amazon Issuing Certificate: Amazon Serial Number: 0AA5C53948DA57341C59556313D492AD Signature: ecdsa-with-SHA256 Valid From: 00:00:00 29 Nov 2022 Valid To: 23:59:59 29 Nov 2023 Key Usage: Digital Signature Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Basic Constraints: CA:FALSE Subject Key Identifier: 59:20:12:9F:A6:A8:B6:E4:A4:B4:1B:E1:2D:53:31:46:19:5A:0C:4D Authority Key Identifier: AE:5C:80:FF:42:97:C9:C3:A0:9B:65:0B:04:CE:9E:71:7F:3E:CE:5A Authority Info Access: OCSP - URI:http://ocsp.sca3b.amazontrust.com CA Issuers - URI:http://crt.sca3b.amazontrust.com/sca3b.crt Subject Alternative Names: DNS:*.iot.eu-central-1.amazonaws.com, DNS:iot.eu-central-1.amazonaws.com
Then the second certificate is an intermediate one:
Common Name: Amazon Issued By: Amazon Issuing Certificate: Amazon Root CA 3 Serial Number: 06DE6553F4712A18312DFDA2B4846D1B36F76E Signature: ecdsa-with-SHA256 Valid From: 22:06:27 16 Jul 2018 Valid To: 22:06:27 16 Jul 2028 Key Usage: Digital Signature, Certificate Sign, CRL Sign Basic Constraints: CA:TRUE, pathlen:0 Subject Key Identifier: AE:5C:80:FF:42:97:C9:C3:A0:9B:65:0B:04:CE:9E:71:7F:3E:CE:5A Authority Key Identifier: AB:B6:DB:D7:06:9E:37:AC:30:86:07:91:70:C7:9C:C4:19:B1:78:C0 Authority Info Access: OCSP - URI:http://ocsp.rootca3.amazontrust.com CA Issuers - URI:http://crt.rootca3.amazontrust.com/rootca3.cer
The root itself is not returned as AWS assumes that the client already has it. But let’s look into it as well:
Common Name: Amazon Root CA 3 Issued By: Amazon Issuing Certificate: Amazon Root CA 3 Serial Number: 066C9FD5749736663F3B0B9AD9E89E7603F24A Signature: ecdsa-with-SHA256 Valid From: 00:00:00 26 May 2015 Valid To: 00:00:00 26 May 2040 Key Usage: Digital Signature, Certificate Sign, CRL Sign Basic Constraints: CA:TRUE Subject Key Identifier: AB:B6:DB:D7:06:9E:37:AC:30:86:07:91:70:C7:9C:C4:19:B1:78:C0
Notice the chain:
- The certificate for the domain has an
Authority Key Identifier:
- The intermediate has a
Subject Key Identifier:
- and an
Authority Key Identifier:
- then the root’s
Subject Key Identifier:
This forms the chain of trust from the domain certificate up to the root CA which is embedded in the device. When AWS presents this chain it proves that it can be trusted and the server side of the connection really ends at Amazon.
Both ends of the connection rely on certificates. While the client-side is more apparent as without that AWS denies connection, verifying the server certificate is similarly important.