Unauthorized exception when using Azure IoT Hub Device Provisioning Service (DPS)

1.6k Views Asked by At

I'm trying to migrate an existing solution with Azure IoT Hub to use Azure IoT Hub Device Provisioning Service (DPS).

The devices authenticate themself using a X.509 Self-Signed certificate.

For testing purposes I can generate a certificate using

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
  • Common Name set to mything1
  • All other fields blank

I can then combine those files into one file using

openssl pkcs12 -inkey key.pem -in certificate.pem -export -out certificate.p12

Choosing password as the password.

I then have 3 files.

  • certificate.p12 containing both certificate and private key
  • certificate.pem containing the certificate
  • key.pem containing the private key

I can then register this device directly in the IoT Hub using

string ThingId = "mything1";

// Register device directly in IoT hub.
{
    var certificate = new X509Certificate2(File.ReadAllBytes("certificate.pem"));
    string thumb1 = certificate.Thumbprint;

    using (var hasher = SHA256.Create())
    {
        using (var registryManager = RegistryManager.CreateFromConnectionString(iotHubConnectionString))
        {
            await registryManager.AddDeviceAsync(new Device(ThingId)
            {
                Authentication = new AuthenticationMechanism()
                {
                    X509Thumbprint = new X509Thumbprint()
                    {
                        PrimaryThumbprint = thumb1,
                        SecondaryThumbprint = thumb1,
                    }
                },
            });
        }
    }
}

Then after the device is registered I can connect to the IoT Hub as the device using the certificate as device authentication.

// Vertifying that certificate is ok etc
// by connecting to an IoT Hub
{
    var auth = new DeviceAuthenticationWithX509Certificate(ThingId, new X509Certificate2(File.ReadAllBytes("certificate.p12"), "password"));

    var client = DeviceClient.Create(iotHubHostname, auth,
        new ITransportSettings[] { new AmqpTransportSettings(Microsoft.Azure.Devices.Client.TransportType.Amqp_WebSocket_Only) });

    // Report a value just to make sure it works.
    TwinCollection props = new TwinCollection();
    props["Hello"] = "World";
    await client.UpdateReportedPropertiesAsync(props, new CancellationTokenSource(1000 * 30).Token);
}

And this works and is the solution I have today. Now trying to do the same thing but using DPS.

First create an individual enrollment for the device:

// Create an individual enrollment for device
using (var provisioningServiceClient = 
    ProvisioningServiceClient.CreateFromConnectionString(provisioningServiceConnectionString))
{
    var attestation = X509Attestation.CreateFromClientCertificates(new X509Certificate2(File.ReadAllBytes("certificate.pem")));

    IndividualEnrollment individualEnrollment =
        new IndividualEnrollment(
                ThingId,
                attestation);

    IndividualEnrollment individualEnrollmentResult =
        await provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment);
}

Then after the enrollment is created, I should be able to provison myself as a device.

string globalDeviceEndpoint = "global.azure-devices-provisioning.net";
            
using (var security = new SecurityProviderX509Certificate(new X509Certificate2(File.ReadAllBytes("certificate.p12"), "password")))
{
    ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(globalDeviceEndpoint, 
        s_idScope, 
        security, 
        new ProvisioningTransportHandlerHttp());
    
    DeviceRegistrationResult result = await provClient.RegisterAsync(); // Throws exception

    Console.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceId: {result.DeviceId}");
}

But this throws an unauthorized exception:

Unhandled exception. Microsoft.Azure.Devices.Provisioning.Client.ProvisioningTransportException: {"errorCode":401002,"trackingId":"f7ec27c8-dbad-4600-8b47-f46aaa0160de","message":"Unauthorized","timestampUtc":"2021-05-20T14:09:00.8491407Z"}
 ---> Microsoft.Rest.HttpOperationException: Operation returned an invalid status code 'Unauthorized'
   at Microsoft.Azure.Devices.Provisioning.Client.Transport.RuntimeRegistration.RegisterDeviceWithHttpMessagesAsync(String registrationId, String idScope, DeviceRegistration deviceRegistration, Nullable`1 forceRegistration, Dictionary`2 customHeaders, CancellationToken cancellationToken)
   at Microsoft.Azure.Devices.Provisioning.Client.Transport.RuntimeRegistrationExtensions.RegisterDeviceAsync(IRuntimeRegistration operations, String registrationId, String idScope, DeviceRegistration deviceRegistration, Nullable`1 forceRegistration, CancellationToken cancellationToken)
   at Microsoft.Azure.Devices.Provisioning.Client.Transport.ProvisioningTransportHandlerHttp.RegisterAsync(ProvisioningTransportRegisterMessage message, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at Microsoft.Azure.Devices.Provisioning.Client.Transport.ProvisioningTransportHandlerHttp.RegisterAsync(ProvisioningTransportRegisterMessage message, CancellationToken cancellationToken)
   at DPSWorkShop.Program.Main(String[] args) in C:\Work\Elux\DPSWorkShop\DPSWorkShop\Program.cs:line 91
   at DPSWorkShop.Program.<Main>(String[] args)

What am I doing wrong here?

EDIT: As answered by Rajeev here: https://stackoverflow.com/a/67641996/6877590 The problem is the basic constraint: CA:true.

To solve this (for testing)

  1. edit the file /usr/lib/ssl/openssl.cnf
  2. Find [ v3_ca ]
  3. Change CA:true to CA:false
1

There are 1 best solutions below

1
On BEST ANSWER

The leaf certificate presented by the device should not contain the 'CA' Basic constraint. See RFC 5280. This is the reason DPS is throwing an 'Unauthorized' exception.