Skip to main content

使用X.509证书来认证设备

分类:  Azure物联网 标签:  #Azure #IoT Hub # 发布于: 2023-06-13 20:39:20

前面学习过如何使用SAS Key来连接和认证设备,我们本节使用X.509证书来连接和认证设备。

本节要点:

  • 生成根证书以及证书链和设备证书
  • Azure IoT Hub注册根证书。
  • 使用证书链签发设备证书
  • 使用.Net SDK通过X.509证书连接设备

生成证书以及证书链,设备证书

我们这里为了测试的目的,因此使用的是工具CA签发的证书,关于签发证书我们也使用两种不同的方式,第一是使用Azure C SDK提供的脚本工具来自动生成,另外一种是使用openssl工具来生成。

使用Azure C SDK提供的脚本工具

要使用该SDK提供的脚本工具,你可以先clone源代码,或者仅仅下载该目录也可以,该工具的位置:https://github.com/Azure/azure-iot-sdk-c/tree/master/tools/CACertificates, 该工具详细的使用方法可以参考Readmehttps://github.com/Azure/azure-iot-sdk-c/blob/master/tools/CACertificates/CACertificateOverview.md

你可以选择是powershell或者是bash工具,windows下可以直接使用WSL, 为了方便,我直接使用了WSL + Ubuntu 20.04 并使用该工具提供的bash脚本,来生成必要的证书。

  1. 创建放置生成结果的目录:

     mkdir MyDeviceX509
     cd MyDeviceX509/
     cp ../azure-iot-sdk-c/tools/CACertificates/*.cnf .
     cp ../azure-iot-sdk-c/tools/CACertificates/*.sh .
     chmod a+w *.sh
    

    这样就将需要的脚本和配置文件拷贝到当前的目录下了。查看该目录可以看到仅仅只有三个文件:

    • certGen.sh: 用于生成证书的脚本
    • openssl_root_ca.cnf: 根证书的配置
    • openssl_device_intermediate_ca.cnf: 中间证书的配置

    需要注意的是,如果打开脚本certGen.sh可以观察到默认的证书密钥的密码是1234, 有效期也只有30天,你可以根据自己的需要来更改一下。

  2. 创建证书链:
    使用如下的命令创建证书链:

     ./certGen.sh create_root_and_intermediate
    

    注意
    这里生成的根证书是:./certs/azure-iot-test-only.root.ca.cert.pem

    现在可以将该证书上传到Azure IoT Hub的Portal上了,登录到Azure IoT Hub, 选择左侧的菜单Security Settings -> Certificates, 然后点击Add, 如下图:



  3. 验证根证书
    我们使用了自签发的证书Azure IoT Hub为了验证该证书确实是你签发的,还需要使用一个验证的流程,选择我们刚刚上传的证书,单击它,在出现的界面上,点击按钮Generate verification code, 会生成一个串字符串,将该字符串拷贝出来,如下图:


    然后回到刚刚WSL的bash命令行,使用如下的命令,利用刚刚的字符串加密并生成一个文件:

     ./certGen.sh create_verification_certificate <你拷贝下来的字符串>
    

    运行完成后会在目录./certs/verification-code.cert.pem生成这样一个文件,将这个文件从刚刚那个Portal上的界面传上去就好,然后店家下面的按钮Verity,成功后即完成了根证书的验证。
    使用证书链创建新的设备我们需要注意的是设备使用证书认证,该证书的主要域名必须是该设备的device id!!!

  4. 使用证书链创建新的设备

    我们需要注意的是设备使用证书认证,该证书的主要域名必须是该设备的device id!!!
    回到我们的命令行,使用如下的命令创建一个新设备:

     ./certGen.sh create_device_certificate mydevice
    

    注意后面的mydevice即是我们的device id

    然后要生成证书链:

     cd ./certs && cat new-device.cert.pem azure-iot-test-only.intermediate.cert.pem azure-iot-test-only.root.ca.cert.pem > new-device-full-chain.cert.pem
    

    这样new-device-full-chain.cert.pem就是我们完整的证书链了,将该文件以及设备的包含私钥证书文件:./private/new-device.cert.pfx拷贝出来并备份,我们之后需要使用到这两个文件。

到这里我们需要准备的证书以及设备签发都已经准备好了,剩下的我们需要注意的是:基于证书的认证是假设用户可以将证书的私钥安全的保存,如果是设备制造商可以考虑使用硬件的手段来保持设备私钥的安全性,例如添加一个安全芯片就可以了。

关于如何使用openssl直接来创建证书,可以参考文档:https://docs.microsoft.com/zh-cn/azure/iot-hub/tutorial-x509-openssl
https://docs.microsoft.com/zh-cn/azure/iot-hub/tutorial-x509-self-sign

另外需要注意的是,我们这里的证书颁发和自签名证书不是一回事。

创建设备

可以使用如下的Azure Cli IoT扩展命令创建设备mydevice:

az iot hub device-identity create --device-id mydevice --auth-method x509_ca --hub-name MyIoTHubByCli

如果是使用Azure Portal创建设备,记得Authentication type选择为X.509 CA Signed

创建完成后,我们开始编写代码来使用代码连接该设备,并向iot hub发送遥测数据。

创建应用

通过Azure Cli IoT扩展工具找到连接字符串。

az iot hub device-identity connection-string show --device-id simDevice --hub-name MyIoThubByCli

该语句返回的结果就是我们需要的连接字符串,那到该以SAS Key为基础的连接字符串之后,我们可以使用如下的代码来连接Azure IoT Hub以及向Azure IoT Hub发送遥测数据。

注意
代码是基于.Net 6

使用.Net SDK连接到Azure IoT Hub

使用如下的命令创建一个项目,并引入需要的SDK包:

dotnet new console -o MyDevice
cd MyDevice
dotnet add package Microsoft.Azure.Devices.Client
code .

打开VS code或者其他编辑器之后,在根目录下添加一个文件GlobalUsing.cs, 打开该文件,添加如下的引用:

global using Microsoft.Azure.Devices.Client;
global using System.Text.Json;
global using System.Text;
global using System.Security.Cryptography.X509Certificates;

然后打开文件Program.cs, 以如下的内容进行替换:

Console.WriteLine("使用X.509证书连接Azure IoT Hub实例");

var IoTHubHostName = "MyIoTHubByCli.azure-devices.cn";

var fullChainPath = @"D:\MyProjects\azure-demo\IoT\MyDevice\new-device-full-chain.cert.pem";
var deviceCertPath = @"D:\MyProjects\azure-demo\IoT\MyDevice\new-device.cert.pfx";

var chainCert = new X509Certificate2Collection();
chainCert.Add(new X509Certificate2(fullChainPath));
var deviceCert = new X509Certificate2(deviceCertPath, "1234");

var auth = new DeviceAuthenticationWithX509Certificate("mydevice", 
    deviceCert,
    chainCert
);

DeviceClient s_deviceClient = DeviceClient.Create(
    IoTHubHostName,
    auth,
    TransportType.Mqtt_Tcp_Only
);


Console.WriteLine("请同时按 Control - C 退出应用");

using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (sender, eventArgs) => 
{
    eventArgs.Cancel = true;
    cts.Cancel();
    Console.WriteLine("退出程序......");
};

await SendDeviceToCloudMessageAsync(cts.Token);

await s_deviceClient.CloseAsync();

s_deviceClient.Dispose();
Console.WriteLine("模拟设备退出!");

async Task SendDeviceToCloudMessageAsync(CancellationToken ct)
{
    //初始化遥测的配置
    double minTemperature = 20;
    double minHumidity = 60;
    var rand = new Random();

    while ( !ct.IsCancellationRequested )
    {
        double currentTemperature = minTemperature + rand.NextDouble() * 15;
        double currentHumidity = minHumidity + rand.NextDouble() * 20;

        //创建Json编码的消息
        string messageBody = JsonSerializer.Serialize(
            new {
                temperature = currentTemperature,
                humidity = currentHumidity,
            }
        );

        using var message = new Message(Encoding.ASCII.GetBytes(messageBody))
        {
            ContentType = "application/json",
            ContentEncoding = "utf-8",
        };

        message.Properties.Add("temperatureAlert", (currentTemperature> 30 )? "true":"false");

        await s_deviceClient.SendEventAsync(message);
        Console.WriteLine($"{DateTime.Now} > 发送消息:{messageBody}");

        await Task.Delay(1000);

    }
}

查看上述代码,需要注意几个地方:X.509证书认证仅仅支持协议:Amqp_Tcp_Only and Mqtt_Tcp_Only, 只有在打开preview的特性才会全部都支持。

运行上述代码,即可得到如下的结果:

D:\MyProjects\azure-demo\IoT\MyDevice>dotnet run
使用X.509证书连接Azure IoT Hub实例
请同时按 Control - C 退出应用
2021/11/14 19:09:17 > 发送消息:{"temperature":22.006785013663343,"humidity":79.33649871732202}
2021/11/14 19:09:18 > 发送消息:{"temperature":29.212889622031867,"humidity":67.02008618983794}
2021/11/14 19:09:19 > 发送消息:{"temperature":27.165354023828023,"humidity":61.565778219321615}
2021/11/14 19:09:20 > 发送消息:{"temperature":23.34750919709292,"humidity":78.84984446542757}
2021/11/14 19:09:21 > 发送消息:{"temperature":23.353157527450882,"humidity":69.36842286068911}
2021/11/14 19:09:22 > 发送消息:{"temperature":24.589849517013082,"humidity":74.7824047858612}
2021/11/14 19:09:23 > 发送消息:{"temperature":20.2638167724549,"humidity":66.07900909746525}
2021/11/14 19:09:24 > 发送消息:{"temperature":22.376024181331882,"humidity":75.86504819981639}
2021/11/14 19:09:25 > 发送消息:{"temperature":22.512279572665342,"humidity":77.10615306692475}
2021/11/14 19:09:26 > 发送消息:{"temperature":25.78862849456724,"humidity":65.04305993271309}
2021/11/14 19:09:27 > 发送消息:{"temperature":22.57763417271473,"humidity":63.81132583056489}
2021/11/14 19:09:28 > 发送消息:{"temperature":32.47645238697139,"humidity":72.46849600281405}
2021/11/14 19:09:29 > 发送消息:{"temperature":20.451489868968316,"humidity":79.10260041491392}