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

问题描述

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

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

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

生成证书
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
  • 通用名称设置为 mything1
  • 所有其他字段为空

然后我可以使用

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

选择 password 作为密码

然后我有 3 个文件

  • certificate.p12 包含证书和私钥
  • certificate.pem 包含证书
  • key.pem 包含私钥

然后我可以使用

直接在 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 中心作为设备。

// 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,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 在这里回答的那样:https://stackoverflow.com/a/67641996/6877590 问题是基本约束:CA:true。

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

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

解决方法

设备提供的叶证书不应包含“CA”基本约束。见RFC 5280。这就是 DPS 抛出“未经授权”异常的原因。