How To Authenticate With Certificates to Azure IoT Hub

Prerequisites

  1. (PKI Admin) Created a Subordinate CA in Azure

Overview - How to Authenticate with Certificates to Azure IoT Hub

X509 Certificates are the most secure way for IoT devices to authenticate with Azure IoT Hub. In this tutorial, we will show you how to create an Certificate Authority in Azure using EZCA and connect your IoT devices to Azure IoT Hub in a secure and compliant way.

How To Connect IoT Devices to Azure IoT Hub - Video Version

Creating The Azure IoT Hub Instance

For this tutorial we only need an Azure IoT hub.

  1. First go to the Azure portal
  2. Click on create a resource. Create Azure IoT Hub
  3. Enter “IoT Hub” in the search bar and press enter.
  4. Click create. Create Azure IoT Hub Set Details
  5. Enter the resource group where you want to create this IoT hub.
  6. Enter a name for your IoT Hub.
  7. Click Review + Create Create IoT Hub in Azure
  8. Click Create
  9. Once the resource is created, go to your resource. Azure IoT Hub Screenshot

Establish Trust Between IoT Hub and Your EZCA CA

After creating the Azure resource, the next step is to establish a trust between Azure and our Certificate Authority. This trust tells Azure to trust any certificate issued by this Certificate Authority as a valid credential for this IoT Hub.

This can be done with our one click trust in the EZCA portal as well as manually in the Azure portal. First we will demonstrate how to do it with EZCA’s one click trust (this does not only make it easier to implement, but EZCA also can take care of device deactivation if a certificate is revoked) and then option B manually in Azure.

Option A: Adding IoT Hub Certificate Authentication with EZCA One Click Trust

This Option will add automatically add your CA to Azure IoT Hub and will automatically update the new CA certificates to Azure IoT hub when the CA is renewed. Additionally, since Azure IoT does not follow the RFC 5280 standard for certificate revocation, EZCA will automatically disable the device in Azure IoT Hub when a certificate is revoked.

  1. Prerequisite: Connecting EZCA to your IoT Hub
  2. In a new tab, go to the EZCA portal
  3. Navigate to the Certificate Authorities Page View Your Azure PKI CAs
  4. Press the “View Details” button on the CA you will use to issue certificates for your IoT devices. View CA Details
  5. Scroll down to “CA Integrations”
  6. Select the Azure Subscription and IoT Hub you want to connect
  7. If you want to enable automatic device deactivation when a certificate is revoked, select the “Disable IoT device when certificate is revoked” checkbox. learn why this is necessary for Azure IoT Hub Certificate revocation.
  8. Click “Connect CA to IoT Hub” Connect CA to IoTHub

    For EZCA to be able to disable your devices in Azure, The Keytos Application requires the IoT Hub Registry Contributor permission in your Azure IoT Hub Instance.

  9. EZCA will now add all the locations of that CA to IoT Hub and will automatically update the new CA certificates to Azure IoT hub when the CA is renewed. Connect CA to IoTHub

Option B: Manually Add Certificate Authority To Azure IoT Hub

  1. In a new tab, go to the EZCA portal
  2. Navigate to the Certificate Authorities Page CA Menu
  3. Press the “View Details” button on the CA you will use to issue certificates for your IoT devices. View CA Details
  4. Press the “Download Certificate” button. This will download a copy of your CAs certificate. Download CA Certificate in Azure PKI
  5. Once you have downloaded the certificate, go back to the Azure IoT Hub tab. Azure IoT Hub
  6. Navigate to the Certificates page, under the Security Settings Menu. Add CA Certificate Azure IoT Hub
  7. Press the “Add” Button Add CA Certificate Azure IoT Hub
  8. Enter a name for you to recognize this CA in Azure.
  9. Upload the certificate you just downloaded from EZCA.
  10. Select the checkmark to set certificate status to verified (This attests that you can issue certificates from that CA).
  11. Press the “Save” button. Link IoT Hub and EZCA
  12. This will add the CA as a trusted CA for Azure. You can repeat these steps with as many CAs you want to add. If you have multiple locations of the Same CA, you must add all the certificates for that specific CA.

Using The Sample Code To Connect To Azure IoT Hub in C#

Now that we have created our Azure IoT Hub and established trust with the EZCA CA, we will download a sample repository that will: register the domain in EZCA, create a certificate, use the certificate to authenticate to Azure. This sample is a great starting point to having a POC for certificate based authentication in Azure IoT Hub.

  1. In a new tab, go to our GitHub Azure IoT Samples Repository
  2. Clone or download the repository. Clone Azure IoT Hub Certificate Authentication Sample
  3. Open the SimulateIoTHubDevice.csproj project located in AzureIoTSamples -> SimulateIoTHubDevice.

    This project has to be run on Linux or Mac due to a limitation that .NET in Windows requires you to use the windows cert store to use X509 Certificates. If you are in a Windows workstation, we recommend running this in VS Code WSL. Alternatively, you can modify the sample with our Windows Cert Store Sample to use the Windows store to create the certificates (not recommended since your IoT devices will not have a Windows store and you will have to rewrite the code again).

    Simulate IoT Device Code to Connect to Azure IoT Hub in VSCode
  4. For this sample to work, all you have to do is change the variable _iotHubEndpoint to match the IoT Hub endpoint of the resource you created at the beginning of this guide.
    string _iotHubEndpoint = "YOURENDPOINT.azure-devices.net";

How to Add Your First Simulated Device to Azure IoT Hub

The sample program in SimulateIoTHubDevice.csproj will create a fake device ID to simulate a new device, then it will register the device in Azure and set it up for Certificate Based Authentication with a trusted CA (Certificate Authority), this will allow you to automatically renew the certificate without having to register the device or device keys each time a new certificate is created.

  1. Once you change the endpoint run the project.
  2. Press the “Terminal” Tab in VSCode. How to Connect to Azure IoT Hub with X509 Certificates Using C#
  3. If you have more than one available CA in EZCA, it will ask you to select the one you want to issue the certificate from.

    This CA has to be one of the CAs you added to IoT Hub in the “Establish Trust Between IoT Hub and Your EZCA CA” step.

    Simulate Device Code
  4. Enter the desired number and press enter. Simulate Device Code
  5. The simulation program will then create a fake device ID to simulate a new device and ask you to register it in Azure.

How To Add The Simulated Device To Azure IoT Hub with CA Authentication In C#

This sample code now contains code to automatically register the device in Azure IoT Hub. This is done by using the Azure IoT Hub SDK to register the device in Azure IoT Hub. This is done in the following lines:

//Register Device in Azure IoT Hub
var registryManager = RegistryManager.Create(_iotHubEndpoint, 
new DefaultAzureCredential());
var device = new Microsoft.Azure.Devices.Device(deviceID)
{
    Authentication = new AuthenticationMechanism()
    {
        Type = AuthenticationType.CertificateAuthority
    }
};
var deviceWithKeys = await registryManager.AddDeviceAsync(device);

If you are going to use the automatic device provisioning code in the sample, the account you are using to run the code must have the permission (IoT Hub Registry Contributor) to register devices in Azure IoT Hub.

This will register the device with Azure IoT Hub and set it up for Certificate Based Authentication with a trusted CA (Certificate Authority), as you can see we are registering it even before creating the keys, since this uses certificate based authentication with the subject name of the certificate, this will allow you to automatically renew the certificate without having to register the device or device keys each time a new certificate is created.

//Console.WriteLine($"Please register your device in Azure. Device ID: {deviceID}");
//Console.WriteLine("Press Enter to continue..");
//Console.ReadLine();

Adding The Simulated Device To Azure IoT Hub Manually

If you prefer to add the device manually, you can comment out the code above and follow uncomment the following code so the program stops once the device ID is create:

  1. Copy the Device ID from the terminal. Simulate Device Code
  2. Go back to the Azure IoT Hub tab. Add IoT Device to Azure IoT Hub
  3. Navigate to the Devices page, under the Device Management Menu. View Azure IoT Hub Devices
  4. Click on the “Add Device” button. how to Add IoT Device to Azure IoT Hub
  5. Enter the device ID you previously copied from the sample program. Add IoT Device to Azure
  6. Select “X.509 CA Signed” as the authentication type. Add IoT Device to Azure with Certificate Authority Authentication
  7. Click the Save button. Add IoT Device to Azure IoT Hub
  8. This will create the device in Azure. Allowing Azure to send and receive messages from this device. View Azure IoT Hub Devices

Getting the certificate for your Azure IoT Device

  1. Once the device is registered in Azure, we are ready to continue with the program.
  2. Go Back to VS Code, put your curser on the terminal end press enter. Simulate Device Code for IoT Hub Certificate Authentication
  3. The simulator will then reach out to EZCA, create a new certificate.
  4. Then it will send 5 randomly generated temperature readings to Azure. Sending data to Azure IoT Hub With Certificate Based Authentication
  5. You can now stop the debugger.
  6. Going back to the Azure portal we can see the messages have been received by Azure Azure showing messages received after Azure IoT Hub Certificate based authentication

Understanding The Code Sample To Connect To Azure IoT Hub in C#

While having a working code sample is great, we recommend going in and understanding the details of what the code is doing. For this I am going to open the solution in Visual Studio to also see the EZCASharedLibrary.csproj library that this project depends on to create the certificates.

  1. Open the SimulateIoTHubDevice.sln solution in Visual Studio or your preferred text editor.
  2. This solution has 3 projects:
    1. EZCASharedLibrary.csproj This project is a library to call EZCA and manage all the cryptographic operations
    2. SimulateIoTHubDevice.csproj The project used for this tutorial (It depends on EZCASharedLibrary for cryptographic operations)
    3. SimulateDeviceProvisioning.csproj This Project is covered in our guide for using Azure’s Device Provisioning Service. It is basically this project + the code to automatically provision devices.
  3. Open the program.cs located in the SimulateIoTHubDevice.csproj project. Code Deep Dive for Azure IoT Hub Certificate Authentication

Registering the Device in Azure IoT Hub

we can see that after the initializations, the code begins with with the creation of a device id (to simulate it we just create a random guid), and then we are registering the device in Azure IoT Hub. This is done in the following lines:

  1. We first create teh device ID
        //Create a random device ID
        string deviceID = Guid.NewGuid().ToString();
    
  2. Then we initialize the Azure IoT Hub SDK Registry Manager (If Registering multiple devices this has to be done only once)
        //Register Device in Azure IoT Hub
        var registryManager = RegistryManager.Create(_iotHubEndpoint, new DefaultAzureCredential());
    
  3. Then we create the device object with the device ID and set the authentication type to Certificate Authority. (As you can see there is no credentials being saved, this basically tells Azure IoT Hub that this device will authenticate with a certificate issued by a trusted CA with the device id in the subject name of the certificate)
        var device = new Microsoft.Azure.Devices.Device(deviceID)
        {
            Authentication = new AuthenticationMechanism()
            {
                Type = AuthenticationType.CertificateAuthority
            }
        };
    
  4. Then we call the Azure IoT Hub SDK to register the device in Azure IoT Hub.
        var deviceWithKeys = await registryManager.AddDeviceAsync(device);
    

Selecting Issuing CA

This part of the code is for usability of the sample, in real production scenario, the CA would have been selected in advance and passed as a setting to the registration program.

Getting Available CAs

  1. First we call ezManager.GetAvailableCAsAsync() if we deep dive into this function, we can see that it gets a token and then calls an API to get the available CAs. Let’s dive into the get token function.
    private async Task<string> GetTokenAsync()
    {
        if (_token == null || _token.Value.ExpiresOn < DateTime.UtcNow)
        {
            //Create Azure Credential 
            //This example includes Interactive for development purposes. 
            //For production you should remove interactive since there is no human involved
            DefaultAzureCredential credential = new(includeInteractiveCredentials: true);
            TokenRequestContext authContext = new(
                new string[] { "https://management.core.windows.net/.default" });
            _token = await credential.GetTokenAsync(authContext);
        }
        return _token.Value.Token;
    }
    

    As we can see, this function uses Azure default credentials which will try to use an MSI and then your developer credentials. This is great for development since it allows you to use your person identity to authenticate as a machine. However, as we outline in our guide IoT Security Best Practices the IoT devices should be issued by a machine. If that machine is an Azure computer, no need to change this code. If the machine programming your IoT devices is not an Azure VM, You will have to change this authentication code to use an Client Certificate Credential

  2. Once the token is created, ezManager.GetAvailableCAsAsync() will call the EZCA api to get the available SSL CAs that that user can request from.
        HttpResponseMessage response = await _httpService.GetAPIAsync($"{_portalURL}api/CA/GetAvailableSSLCAs", await GetTokenAsync());
    
  3. If the call is successful, it will return a list of Available CAs.

Selecting Issuing CA

  1. If more than one CA is returned, the user is given the choice of selecting the CA, since this is not important for this tutorial, we will skip this section.

Registering Device in EZCA

Once the CA is selected, we have to create a fake DeviceID and register it as a domain for the CA en EZCA. This is all done in the RegisterDomainAsync function.

  1. First we get the token (this code was covered in a previous section so we will skip it this time around).
  2. Then we use the token information to get the user requesting the domain. This is done to add the user as a requester and an owner of the domain. In production, we recommend having owners and requesters designated by the security team.
  3. After we have the owners and requesters list, we create a NewDomainRegistrationRequest object, this object will then be send as a post request to EZCA. Below you can see an explanation of each of the fields.
        public class NewDomainRegistrationRequest
        {
    
            [JsonPropertyName("CAID")]
            public string? CAID { get; set; }  //This is the CA ID used by EZCA to know which CA you are requesting from. We got this value from the CA Information.
            [JsonPropertyName("TemplateID")]
            public string? TemplateID { get; set; } // This is another ID Value that allows EZCA to know which CA you are requesting from. We got this value from the CA Information.
            [JsonPropertyName("Domain")]
            public string? Domain { get; set; } // In this case the domain is the device ID of the Device
            [JsonPropertyName("Owners")]
            public List<AADObjectModel> Owners { get; set; } = new List<AADObjectModel>(); //This are the AAD Objects that are allowed to make changes to this domain. 
            [JsonPropertyName("Requesters")]
            public List<AADObjectModel> Requesters { get; set; } = new List<AADObjectModel>(); //This are the AAD objects allowed to request certificates for this domain. For this field we recommend keeping it to your enrollment agent only. After the first certificate is issued, your IoT device can use that certificate to renew its certificate.
        }
    
  4. Once we have created the NewDomainRegistrationRequest object, we send the post request to EZCA.
        HttpResponseMessage response = await _httpService.PostAPIAsync($"{_portalURL}api/CA/RegisterNewDomain", JsonSerializer.Serialize(request), await GetTokenAsync());
    

Requesting A Certificate In EZCA

Once the “domain” for this device ID is registered in EZCA, we can go ahead and request the certificate. To create the certificate, we call ezManager.RequestCertificateAsync(selectedCA, deviceID); This function will create a certificate signing request, request the certificate and return the newly created X509 certificate.

  1. First we create an RSA key.
        //create a 4096 RSA key
        RSA key = RSA.Create(4096);
    
  2. Then we crete the certificate signing request (CSR) this is used to request a certificate from the CA proving that you are the owner of the private key without sharing the private key with the CA.
        //create Certificate Signing Request 
        X500DistinguishedName x500DistinguishedName = new("CN=" + domain);
        CertificateRequest certificateRequest = new(x500DistinguishedName, key,
            HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        string csr = PemEncodeSigningRequest(certificateRequest);
    
  3. Then we create a list of subject alternate names, in this case we are only adding the device ID.
        List<string> subjectAlternateNames = new()
        {
            domain
        }; 
    
  4. Then we set the certificate lifetime, in this example we are setting it for 10 days, for production you will want to talk to your security team to see what is an acceptable certificate lifetime for your certificates.
        int certificateValidityDays = 10; // setting the lifetime of the certificate to 10 days
    
  5. Once we have all the required information we create a CertificateCreateRequestModel object, this object will then be send as a post request to EZCA. Below you can see an explanation of each of the fields.
        public class CertificateCreateRequestModel
        {
    
            [JsonPropertyName("SubjectName")]
            public string SubjectName { get; set; } //This is the subject name of the certificate, for this case we set CN=DeviceID where DeviceID is your device ID. This is used by Azure to know which device is calling.
            [JsonPropertyName("SubjectAltNames")]
            public List<string> SubjectAltNames { get; set; } = new List<string>(); //This are alternative names for the subject name, this was introduced to have multiple domains in one certificate. Since some authentication standards use subject alternate names for authentication, we add the device ID as a subject alternate name.
            [JsonPropertyName("CAID")]
            public string? CAID { get; set; }  //This is the CA ID used by EZCA to know which CA you are requesting from. We got this value from the CA Information.
            [JsonPropertyName("TemplateID")]
            public string? TemplateID { get; set; } // This is another ID Value that allows EZCA to know which CA you are requesting from. We got this value from the CA Information.
            [JsonPropertyName("CSR")]
            public string CSR { get; set; } //this is the signing request we created to prove we own the private key.
            [JsonPropertyName("ValidityInDays")]
            public int ValidityInDays { get; set; } //this is the number of days the certificate will be valid 
            [JsonPropertyName("EKUs")]
            public string[] EKUs { get; set; } = new string[] { "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2" }; //this is what the certificate can be used for. By default we add Client authentication and Server authentication
            [JsonPropertyName("KeyUsages")]
            public string[] KeyUsages { get; set; } = new string[] { "Key Encipherment", "Digital Signature" }; // this is what the certificate's key can be used for.
            //The following fields are used to give visibility to your administrators how you are using the certificate
            [JsonPropertyName("SelectedLocation")]
            public string SelectedLocation { get; set; } = "IoT Device"; // where the device is being stored.
            [JsonPropertyName("ResourceID")]
            public string ResourceID { get; set; } = string.Empty; //This is only used when the certificate is being stored in Azure
            [JsonPropertyName("SecretName")]
            public string SecretName { get; set; } = string.Empty; //This is only used when the certificate is being stored in Azure
            [JsonPropertyName("AKVName")]
            public string AKVName { get; set; } = string.Empty;//This is only used when the certificate is being stored in Azure
            [JsonPropertyName("AutoRenew")]
            public bool AutoRenew { get; set; } = false; //This indicates if you want EZCA to automatically renew your certificate. This feature is only available when the certificate is being stored in Azure
            [JsonPropertyName("AutoRenewPercentage")]
            public int AutoRenewPercentage { get; set; } = 80; //This feature is only available when the certificate is being stored in Azure
        }
    
  6. After we create the CertificateCreateRequestModel object we send the certificate request to Azure.
        //Request Certificate from EZCA
        HttpResponseMessage response = await _httpService.PostAPIAsync($"{_portalURL}api/CA/RequestSSLCertificate",
            JsonSerializer.Serialize(request), await GetTokenAsync());
    
  7. Once the certificate is created, we return the certificate with it’s private key.
        APIResultModel? result = JsonSerializer.Deserialize<APIResultModel>(await response.Content.ReadAsStringAsync());
        if (result != null)
        {
            if(result.Success)
            {
                X509Certificate2 certificate = ImportCertFromPEMString(result.Message);
                return certificate.CopyWithPrivateKey(key);
            }
        }
    

How to Send Messages to Azure IoT Hub Using Certificate Based Authentication

Now that we have created the certificate, all we have to do is authenticate to Azure and send some messages to test that it is working.

To Authenticate with Azure we will need the Microsoft.Azure.Devices.Client NuGet package.

  1. First we create a DeviceAuthenticationWithX509Certificate with the device ID and the certificate issued for that device.
        DeviceAuthenticationWithX509Certificate auth = new (deviceID, deviceCertificate);
    
  2. Then we create a device client using: the IoT Hub endpoint, the authentication object we just created, and the transport type you want to use.
        var deviceClient = DeviceClient.Create(_iotHubEndpoint, auth, TransportType.Mqtt);
    
  3. Once we have created the device client, we can start sending messages to Azure. To do this we use the following code taken from https://docs.microsoft.com/en-us/azure/iot-hub/tutorial-x509-test-certificate
        static async Task SendEventAsync(DeviceClient deviceClient, string deviceId)
        {
            //ref https://docs.microsoft.com/en-us/azure/iot-hub/tutorial-x509-test-certificate
            string dataBuffer;
            int MESSAGE_COUNT = 5;
            Random rnd = new Random();
            float temperature;
            float humidity;
            int TEMPERATURE_THRESHOLD = 30;
            Console.WriteLine("Device sending {0} messages to IoTHub...\n", MESSAGE_COUNT);
            // Iterate MESSAGE_COUNT times to set random temperature and humidity values.
            for (int count = 0; count < MESSAGE_COUNT; count++)
            {
                // Set random values for temperature and humidity.
                temperature = rnd.Next(20, 35);
                humidity = rnd.Next(60, 80);
                dataBuffer = string.Format("{{\"deviceId\":\"{0}\",\"messageId\":{1},\"temperature\":{2},\"humidity\":{3}}}",
                    deviceId, count, temperature, humidity);
                Message eventMessage = new Message(Encoding.UTF8.GetBytes(dataBuffer));
                eventMessage.Properties.Add("temperatureAlert",
                    (temperature > TEMPERATURE_THRESHOLD) ? "true" : "false");
                Console.WriteLine("\t{0}> Sending message: {1}, Data: [{2}]",
                    DateTime.Now.ToLocalTime(), count, dataBuffer);
    
                // Send to IoT Hub.
                await deviceClient.SendEventAsync(eventMessage);
            }
        }
    

How To Renew Azure IoT Hub Certificates

Since we set the authentication to be certificate based with a trusted certificate authority (CA), we can now renew the certificate without having to register the device again. This can be done straight from your IoT device calling the EZCA Renew API, If you are using c# you can use our EZCA NuGet Package or your can see the code on how we call the API to adapt it to your language of choice. If you have any questions, contact the Keytos team and one of our engineers will be happy to help you.

How To Revoke Azure IoT Hub Certificate

Reading The Azure IoT Documentation you probably came across Microsoft mentioning that they do not follow the RFC 5280 standard for certificate revocation. This means that if you revoke a certificate in Your CA, Azure IoT Hub will not know that the certificate is revoked and will continue to allow the device to authenticate. This is why we recommend using EZCA’s one click trust to connect your CA to Azure IoT Hub. EZCA is the only Certificate Authority that is deeply integrated with Azure IoT Hub and will automatically disable the device in Azure IoT Hub when a certificate is revoked. To Enable this feature, follow the steps in Establish Trust Between IoT Hub and Your EZCA CA and make sure to check the “Disable IoT device when certificate is revoked” checkbox.