When using group enrollments with the Azure IoT Hub Device Provisioning Service (DPS), it is necessary with a X.509 certificate infrastructure.
At the very least, it is necessary to have a root Certificate Authority (Root CA) that can be used to sign device certificates.
The devices authenticate with the DPS using a device certificate which is signed by the root CA. That way, the DPS can verify that the device is allowed to connect to the DPS and be provisioned for communicating with the Azure IoT Hub.
It is better if the root CA is used to sign an intermediate CA certificate, which in turn can be used to sign the device certificates. Depending on the need, there can be a chain of intermediates.
The reason for the intermediate certificates is that it makes it easier to see who or what signed a device certificate. If it should become necessary to revoke the intermediate, only a subset of device certificates will stop functioning.
While it is possible to use a public CA as the root CA, it is not necessary. It is possible to create a root CA certificate on your own and use that for DPS. The whole area of X.509 certificates is complicated, so it may be a good idea to consult with professionals in the field before rolling your own CA.
This blog post is the first part of a series that will include C# code to create a certificate chain. The finished project also includes a sample showing how to let an IoT Hub device provision itself without factory-installed certificates or Hardware Security Module based tokens.
My foray into DPS, certificates, group enrollments etc. came from the need to try out IoT Hub for use in a Windows client. The client was a WPF application, which was going to need some data transfer with a backend. Instead of rolling our own data receival pipeline with web apps or functions, Event Hub etc. we wanted to use an infrastructure capable of receiving millions of events and two-way communication, so the backend could send messages back to the clients.
The Windows client is installed on client computers around the world. Where Azure IoT Hub is (as the name implies) designed for “Internet of Things” devices, there is no reason it can’t be used for software clients.
The main problem was that IoT Hub devices, whether they use DPS or not, are meant to be programmed with some sort of authentication token at the device factory. The token can be either an X.509 certificate or a token stored in a hardware security module (HSM).
When our Windows client software is installed, there is no per-user unique token. The client software would need to generate its authentication token itself.
Since we don’t need to be able to uniquely identify the clients before they register with IoT hub, we could let the client software generate their own authentication token.
This can be done with the DPS’ “Group Enrollment” feature, where any client/device that presents an X.509 certificate which is signed by a certificate which is trusted by the group enrollment’s certificate can be provisioned.
Chain of trust
X.509 certificates can be used as credentials that someone is who they say they are. A certificate is generated using a public/private encryption key pair.
Anyone can create a certificate and use it to authenticate with a third party. But if the certificate is not signed by some whom the third-party trusts, they can’t be sure that whoever presents the certificate is who they claim to be.
The trust relationship is called the “chain of trust”. In its most simple form it works like this:
The root certificate trusts itself. It is generated by the Root CA. They guard their private keys closely, so no one else can create certificates in their name. Typically, the Root CA certificates are installed in client computers, browsers, etc. so they use them to verify other certificates.
Since the Root CA is the “crown jewels” of the certificate authority, they normally create a number of intermediate certificates. These, while still sensitive, can be revoked without damaging the whole chain of trust. A certificate must be revoked if its underlying private key is made known to third parties.
The chain of trust between the first intermediate certificate up to the root certificate is established when the intermediate certificate is cryptographically signed using the root certificates private key.
That way, when someone who has knowledge of the root certificate is presented with the intermediate certificate, they can verify that the root CA vouches for whoever presents the intermediate certificate. They simply look at the intermediate certificate and see who signed it. If the signer is someone they trust, then they can also trust the intermediate certificate.
The same thing happens for the end-entity certificates. They are sometimes called “leaf” certificates, because they are the outermost nodes in the tree of root certificates, intermediates etc.
The leaf certificate is signed by an intermediate certificate. Then whoever wants to check the validity of the leaf certificate can verify the signer’s identity. Even if they don’t know the intermediate certificate, they can check the signer of the intermediate certificate, and so on all the way along the chain of trust until they reach an intermediate certificate they trust or the root certificate.
If none of the certificates in the chain are trusted beforehand, the leaf certificate cannot be verified.
Azure IoT Hub Device Provisioning Service certificates
So that was a rather lengthy explanation of chains of trust. How does it apply to the Azure IoT Hub DPS?
The DPS wants to verify that new devices which want to provision themselves are trustworthy. The DPS doesn’t want to allow just any device to connect. Therefore, the device must authenticate with a certificate (or a HSM token, but that’s not in scope for this text, and they can’t be used for group enrollments either).
Instead of allowing just anyone with a certificate to connect, the DPS validates the chain of trust.
When the device presents its certificate, the DPS looks at the signer of that certificate, and at the signer of that certificate until it reaches one that the DPS trusts. Then DPS knows that it can allow the device to authenticate.
The DPS is configured with a root certificate (or an intermediate, but it doesn’t matter which for this explanation). It may be configured with multiple roots in case the setup requires that. The Microsoft documentation describes a scenario where different device factories add certificates to devices signed by different roots. In case the production at one factory needs to be stopped, their signing certificate can be removed from DPS. Then those devices can no longer connect.
Creating certificates for use by Device Provisioning Service
Since X.509 certificates are a complicated subject, Microsoft have provided a number of walkthroughs along with script samples to generate root certificates, intermediates, and device certificates.
The one I found best is the one that is part of the C language Azure IoT SDK. It is located at https://github.com/Azure/azure-iot-sdk-c/blob/master/tools/CACertificates/CACertificateOverview.md.
Heed the warnings about not using the generated certificates for production. Note that the guide says to be local administrator in Windows. If you replace all occurrences of “LocalMachine” with “CurrentUser” in the “ca-certs.ps1” file, then you don’t need to be admin.
The samples all require an installation of OpenSSL, PowerShell skills etc. That is unfortunate, but as I’ve mentioned several times: This is complicated stuff. These samples are better than nothing. I’ll try to explain what is necessary below.
In the above sample, there is a PowerShell script that generates a root certificate and three intermediaries in a chain. This is step 2 in the guide. Follow it now.
When installed in the Windows certificate store, it is possible to see the chain of trust:
The Intermediate 3 is signed by Intermediate 2, which is signed by Intermediate 1, which is signed by the root CA certificate.
This means that after installing the root certificate in DPS, it will trust a leaf certificate that has been signed by any of the intermediates or by the root certificate.
When uploading a new root certificate to DPS in the Azure Portal, it is marked as “Unverified”:
It must be verified before it can be used. My guess is that Microsoft want to make certain that their customers can sign certificates using the root certificate before they start configuring the enrollments for the DPS. That should alleviate problems which would otherwise occur.
To verify the certificate, the portal can generate a random unique ID. That ID is used to create a new certificate which is signed by the root certificate.
Click the certificate in the list of certificates in the portal to display its properties.
The PowerShell script from the link above has a function to generate a verification certificate:
New-CACertsVerificationCert “12345”
“12345” should be replaced with the verification code from the portal.
This creates a new file called “verifyCert4.cer”. Upload this file to the portal to verify ownership of the root certificate.
After uploading and verifying, the status must change to Verified:
Now the root certificate can be used to create an Enrollment Group.
In the “Manage Enrollments” pane in the portal, click the Add button to open the “Add Enrollment Group” pane.
Give the group a name and select the previously uploaded and verified root certificate.
The rest of the fields in the “Add Enrollment Group” pane are irrelevant for this guide.
When the enrollment group has been created it is present in the list.
Clicking on it will open its details. Later on, you can use that details pane to see which devices have been provisioned using this enrollment group.
Next is the fun part: Actually provisioning a device using the DPS. That will be in my next post on the subject.