使用 Azure IoT 中心设备预配服务 (DPS) 时出现未经授权的异常

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

我正在尝试使用 Azure IoT 中心迁移现有解决方案以使用 Azure IoT 中心设备预配服务 (DPS)。

设备使用 X.509 自签名证书对自己进行身份验证。

出于测试目的,我可以使用

生成证书
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem

然后我可以使用

将这些文件合并为一个文件
openssl pkcs12 -inkey key.pem -in certificate.pem -export -out certificate.p12

选择 password 作为密码。

然后我有 3 个文件。

然后我可以使用

直接在 IoT 中心注册此设备
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,
                    }
                },
            });
        }
    }
}

然后在设备注册后,我可以使用证书作为设备身份验证连接到 IoT Hub。

// 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);
}

这有效,是我今天的解决方案。现在尝试做同样的事情,但使用 DPS。

首先为设备创建个人注册:

// 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);
}

然后在创建注册后,我应该可以将自己配置为设备。

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}");
}

但这会引发未经授权的异常:

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)

我做错了什么?

编辑: 正如 Rajeev 在这里回答的那样: 问题是基本约束:CA:true.

解决这个问题(用于测试)

  1. 编辑文件/usr/lib/ssl/openssl.cnf
  2. 查找[ v3_ca ]
  3. 将CA:true更改为CA:false

设备提供的叶证书不应包含 'CA' 基本约束。参见 RFC 5280。这就是 DPS 抛出 'Unauthorized' 异常的原因。