你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
如何使用 X.509 证书将设备连接到 IoT Central 应用程序
IoT Central 支持共享的访问签名 (SAS) 和 X.509 证书,以保护设备与应用程序之间的通信。 创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程使用 SAS。 本文介绍如何修改代码示例以使用 X.509 证书。 在生产环境中,建议使用 X.509 证书。 有关详细信息,请参阅设备身份验证概念。
本指南介绍 X.509 证书的两种用法 - 组注册(通常在生产环境中使用)和单独注册(适用于测试)。 本文还将介绍如何在证书过期时滚动更新设备证书,以便能够保持连接。
本指南是根据使用 C#、Java、JavaScript 和 Python 的创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程中演示的示例编写的。 有关使用 C 编程语言的示例,请参阅使用注册组预配多个 X.509 设备。
先决条件
若要完成本操作指南中的步骤,应先完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程。 按照本指南中的步骤操作时,可以修改教程中使用的代码。
在本操作指南中,你将生成一些测试 X.509 证书。 若要生成这些证书,需要:
- 安装了 Node.js 6 或更高版本的开发计算机。 若要检查版本,可以在命令行中运行
node --version
。 本教程中的说明假设在 Windows 命令提示符下运行 node 命令。 但是,可以在许多其他的操作系统上使用 Node.js。 - 用于 Node.js 的 Microsoft Azure IoT SDK GitHub 存储库的本地副本,其中包含用于生成测试 X.509 证书的脚本。 使用此链接下载存储库副本:下载 ZIP。 然后将该文件解压缩到本地计算机上的适当位置。
使用组注册
在生产环境中通过组注册使用 X.509 证书。 在组注册中,将根或中间 X.509 证书添加到 IoT Central 应用程序。 具有派生自根证书或中间证书的叶证书的设备可以连接到你的应用程序。
生成根证书和设备证书
在本部分,你将使用 X.509 证书将设备与派生自 IoT Central 注册组证书的证书连接起来。
警告
这种生成 X.509 证书的方法仅用于测试。 对于生产环境,应使用官方的安全机制来生成证书。
导航到你已下载的用于 Node.js 的 Microsoft Azure IoT SDK 中的证书生成器脚本。 安装所需的包:
cd azure-iot-sdk-node/provisioning/tools npm install
创建根证书,然后通过运行脚本派生设备证书:
node create_test_cert.js root mytestrootcert node create_test_cert.js device sample-device-01 mytestrootcert
提示
设备 ID 中可以包含字母、数字和
-
字符。
这些命令会生成以下根和设备证书:
filename | 内容 |
---|---|
mytestrootcert_cert.pem | 根 X509 证书的公共部分 |
mytestrootcert_key.pem | 根 X509 证书的私钥 |
mytestrootcert_fullchain.pem | 根 X509 证书的整个密钥链。 |
mytestrootcert.pfx | 根 X509 证书的 PFX 文件。 |
sampleDevice01_cert.pem | 设备 X509 证书的公共部分 |
sampleDevice01_key.pem | 设备 X509 证书的私钥 |
sampleDevice01_fullchain.pem | 设备 X509 证书的整个密钥链。 |
sampleDevice01.pfx | 设备 X509 证书的 PFX 文件。 |
请记下这些文件的位置。 稍后需要用到此信息。
创建组注册
打开 IoT Central 应用程序并导航到左侧窗格中的“权限”,然后选择“设备连接组”。
选择“+ 新建”,以创建一个名为“MyX509Group”的新注册组,其证明类型为“证书(X.509)”。 可以为 IoT 设备或 IoT Edge 设备创建注册组。
在创建的注册组中,选择“管理主要证书”。
在“主要证书”面板中,选择“添加证书”。
上传前面生成的名为 mytestrootcert_cert.pem 的根证书文件。
如果你使用的是你信任的中间证书或根证书颁发机构,并且知道你拥有证书的完整所有权,则可以通过在上传时将已验证的证书状态设置为“启用”来自行证明你已验证证书。 否则,请在上传时将已验证的证书状态设置为“禁用”。
如果在上传时将已验证的证书状态设置为“禁用”,请选择“生成验证码”。
复制验证码,然后创建 X.509 验证证书。 例如,在命令提示符处执行以下命令:
node create_test_cert.js verification --ca mytestrootcert_cert.pem --key mytestrootcert_key.pem --nonce {verification-code}
选择“验证”,上传已签名的验证证书 verification_cert.pem 以完成验证。
主要证书的状态现在为“已验证”:
你现在可以连接具有派生自此主根证书的 X.509 证书的设备。
保存注册组后,记下 ID 范围。 稍后需要用到此信息。
运行示例代码
如果使用的是 Windows,则 X.509 证书必须位于 Windows 证书存储中,这样才能正常运行该示例。 在 Windows 资源管理器中,双击前面生成的 PFX 文件 - mytestrootcert.pfx
和 sampleDevice01.pfx
。 在“证书导入向导”中,选择“当前用户”作为存储位置,输入 1234
作为密码,然后让向导自动选择证书存储。 向导会将证书导入到当前用户的个人存储中。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
在 IoTHubDeviceSamples Visual Studio 解决方案中,打开 TemperatureController 项目中的 Parameter.cs 文件。
将以下两个参数定义添加到类中:
[Option( 'x', "CertificatePath", HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device PFX file to use during device provisioning." + "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_CERT\".")] public string CertificatePath { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_CERT"); [Option( 'p', "CertificatePassword", HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe password of the PFX certificate file." + "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_PASSWORD\".")] public string CertificatePassword { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_PASSWORD");
保存更改。
在 IoTHubDeviceSamples Visual Studio 解决方案中,打开 TemperatureController 项目中的 Program.cs 文件。
添加以下
using
语句:using System.Security.Cryptography.X509Certificates; using System.IO;
将下列方法添加到类:
private static X509Certificate2 LoadProvisioningCertificate(Parameters parameters) { var certificateCollection = new X509Certificate2Collection(); certificateCollection.Import( parameters.CertificatePath, parameters.CertificatePassword, X509KeyStorageFlags.UserKeySet); X509Certificate2 certificate = null; foreach (X509Certificate2 element in certificateCollection) { Console.WriteLine($"Found certificate: {element?.Thumbprint} {element?.Subject}; PrivateKey: {element?.HasPrivateKey}"); if (certificate == null && element.HasPrivateKey) { certificate = element; } else { element.Dispose(); } } if (certificate == null) { throw new FileNotFoundException($"{parameters.CertificatePath} did not contain any certificate with a private key."); } Console.WriteLine($"Using certificate {certificate.Thumbprint} {certificate.Subject}"); return certificate; }
在
SetupDeviceClientAsync
方法中,将case "dps"
的代码块替换为以下代码:case "dps": s_logger.LogDebug($"Initializing via DPS"); Console.WriteLine($"Loading the certificate..."); X509Certificate2 certificate = LoadProvisioningCertificate(parameters); DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, certificate, cancellationToken); var authMethod = new DeviceAuthenticationWithX509Certificate(dpsRegistrationResult.DeviceId, certificate); deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod); break;
将
ProvisionDeviceAsync
方法替换为以下代码:private static async Task<DeviceRegistrationResult> ProvisionDeviceAsync(Parameters parameters, X509Certificate2 certificate, CancellationToken cancellationToken) { SecurityProvider security = new SecurityProviderX509Certificate(certificate); ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt(); ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, security, mqttTransportHandler); var pnpPayload = new ProvisioningRegistrationAdditionalData { JsonData = PnpConvention.CreateDpsPayload(ModelId), }; return await pdc.RegisterAsync(pnpPayload, cancellationToken); }
保存更改。
若要运行该示例:
将以下环境变量添加到项目中:
IOTHUB_DEVICE_X509_CERT
:<full path to folder that contains PFX files>sampleDevice01.pfx
IOTHUB_DEVICE_X509_PASSWORD
:1234。
生成并运行应用程序。 验证设备预配是否成功。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到包含 pom.xml 文件的 azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample 文件夹,以及用于温度控制器设备示例的 src 文件夹。
编辑 pom.xml 文件,在
<dependencies>
节点中添加以下依赖关系配置:<dependency> <groupId>com.microsoft.azure.sdk.iot.provisioning.security</groupId> <artifactId>${x509-provider-artifact-id}</artifactId> <version>${x509-provider-version}</version> </dependency>
保存更改。
在文本编辑器中打开 src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java 文件。
将
SecurityProviderSymmetricKey
import 语句替换为以下 import 语句:import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider; import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert; import com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException;
添加以下 import 语句:
import java.nio.file.*;
将
SecurityProviderException
添加到main
方法引发的异常列表中:public static void main(String[] args) throws IOException, URISyntaxException, ProvisioningDeviceClientException, InterruptedException, SecurityProviderException {
将
initializeAndProvisionDevice
方法替换为以下代码:private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, SecurityProviderException { String deviceX509Key = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_KEY")))); String deviceX509Cert = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_CERT")))); SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(deviceX509Cert, deviceX509Key, null); ProvisioningDeviceClient provisioningDeviceClient; ProvisioningStatus provisioningStatus = new ProvisioningStatus(); provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityProviderX509); AdditionalData additionalData = new AdditionalData(); additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID)); provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus, additionalData); while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) { if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR || provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED || provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED) { provisioningStatus.exception.printStackTrace(); System.out.println("Registration error, bailing out"); break; } System.out.println("Waiting for Provisioning Service to register"); Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION); } ClientOptions options = new ClientOptions(); options.setModelId(MODEL_ID); if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) { System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri()); System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId()); String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri(); String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId(); log.debug("Opening the device client."); deviceClient = DeviceClient.createFromSecurityProvider(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT, options); deviceClient.open(); } }
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
生成并运行应用程序。 验证设备预配是否成功。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到包含 pnp_temperature_controller.js 应用程序的 azure-iot-sdk-node/device/samples/javascript 文件夹,并运行以下命令以安装 X.509 包:
npm install azure-iot-security-x509 --save
在文本编辑器中打开 pnp_temperature_controller.js 文件。
编辑
require
语句以包含以下代码:const fs = require('fs'); const X509Security = require('azure-iot-security-x509').X509Security;
将以下四行代码添加到“DPS 连接信息”部分以初始化
deviceCert
变量:const deviceCert = { cert: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_CERT).toString(), key: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_KEY).toString() };
通过将第一行替换为以下代码,编辑用于创建客户端的
provisionDevice
函数:var provSecurityClient = new X509Security(registrationId, deviceCert);
在同一函数中,按如下所示修改设置
deviceConnectionString
变量的行:deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';x509=true';
在
main
函数中,在调用Client.fromConnectionString
的行后添加以下行:client.setOptions(deviceCert);
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
执行脚本,验证设备预配是否成功:
node pnp_temperature_controller.js
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到 azure-iot-device/samples/pnp 文件夹,并在文本编辑器中打开 temp_controller_with_thermostats.py 文件。
添加以下
from
语句以导入 X.509 功能:from azure.iot.device import X509
按下面所示修改
provision_device
函数的第一个部分:async def provision_device(provisioning_host, id_scope, registration_id, x509, model_id): provisioning_device_client = ProvisioningDeviceClient.create_from_x509_certificate( provisioning_host=provisioning_host, registration_id=registration_id, id_scope=id_scope, x509=x509, )
在
main
函数中,将用于设置symmetric_key
变量的行替换为以下代码:x509 = X509( cert_file=os.getenv("IOTHUB_DEVICE_X509_CERT"), key_file=os.getenv("IOTHUB_DEVICE_X509_KEY"), )
在
main
函数中,将provision_device
函数调用替换为以下代码:registration_result = await provision_device( provisioning_host, id_scope, registration_id, x509, model_id )
在
main
函数中,将IoTHubDeviceClient.create_from_symmetric_key
函数调用替换为以下代码:device_client = IoTHubDeviceClient.create_from_x509_certificate( x509=x509, hostname=registration_result.registration_state.assigned_hub, device_id=registration_result.registration_state.device_id, product_info=model_id, )
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
执行脚本,验证设备预配是否成功:
python temp_controller_with_thermostats.py
验证遥测数据是否出现在 IoT Central 应用程序的设备视图中:
使用单独注册
通过单独注册使用 X.509 证书来测试设备和解决方案。 在单独注册中,IoT Central 应用程序中没有根或中间 X.509 证书。 设备使用自签名的 X.509 证书连接到应用程序。
生成自签名设备证书
本部分将使用自签名的 X.509 证书连接用于单独注册的设备,单独注册用于注册单个设备。 自签名证书仅用于测试。
警告
这种生成 X.509 证书的方法仅用于测试。 对于生产环境,应使用官方的安全机制来生成证书。
通过运行以下命令创建自签名的 X.509 设备证书:
cd azure-iot-sdk-node/provisioning/tools
node create_test_cert.js device mytestselfcertprimary
node create_test_cert.js device mytestselfcertsecondary
提示
设备 ID 中可以包含字母、数字和 -
字符。
这些命令生成以下设备证书:
filename | 内容 |
---|---|
mytestselfcertprimary_cert.pem | 主要设备 X509 证书的公共部分 |
mytestselfcertprimary_key.pem | 主要设备 X509 证书的私钥 |
mytestselfcertprimary_fullchain.pem | 主要设备 X509 证书的整个密钥链。 |
mytestselfcertprimary.pfx | 主要设备 X509 证书的 PFX 文件。 |
mytestselfcertsecondary_cert.pem | 次要设备 X509 证书的公共部分 |
mytestselfcertsecondary_key.pem | 次要设备 X509 证书的私钥 |
mytestselfcertsecondary_fullchain.pem | 次要设备 X509 证书的整个密钥链。 |
mytestselfcertsecondary.pfx | 次要设备 X509 证书的 PFX 文件。 |
创建单独注册
在 Azure IoT Central 应用程序中,选择“设备”,然后根据恒温器设备模板创建“设备 ID”为“mytestselfcertprimary”的新设备。 记下“ID 范围”以便稍后使用。
打开所创建的设备,然后选择“连接”。
选择“个人注册”作为“身份验证类型”,选择“证书(X.509)”作为“身份验证”方法。
上传以前作为主要证书生成的 mytestselfcertprimary_cert.pem 文件。
上传以前作为次要证书生成的 mytestselfcertsecondary_cert.pem 文件。 再选择“保存” 。
设备现在有一个使用 X.509 证书的单独注册。
运行示例单独注册设备
如果使用的是 Windows,则 X.509 证书必须位于 Windows 证书存储中,这样才能正常运行该示例。 在 Windows 资源管理器中,双击前面生成的 PFX 文件 - mytestselfcertprimary.pfx
和 mytestselfcertsecondary.pfx
。 在“证书导入向导”中,选择“当前用户”作为存储位置,输入 1234
作为密码,然后让向导自动选择证书存储。 向导会将证书导入到当前用户的个人存储中。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
在 IoTHubDeviceSamples Visual Studio 解决方案中,打开 TemperatureController 项目中的 Parameter.cs 文件。
将以下两个参数定义添加到类中:
[Option( 'x', "CertificatePath", HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device PFX file to use during device provisioning." + "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_CERT\".")] public string CertificatePath { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_CERT"); [Option( 'p', "CertificatePassword", HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe password of the PFX certificate file." + "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_PASSWORD\".")] public string CertificatePassword { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_PASSWORD");
保存更改。
在 IoTHubDeviceSamples Visual Studio 解决方案中,打开 TemperatureController 项目中的 Program.cs 文件。
添加以下
using
语句:using System.Security.Cryptography.X509Certificates; using System.IO;
将下列方法添加到类:
private static X509Certificate2 LoadProvisioningCertificate(Parameters parameters) { var certificateCollection = new X509Certificate2Collection(); certificateCollection.Import( parameters.CertificatePath, parameters.CertificatePassword, X509KeyStorageFlags.UserKeySet); X509Certificate2 certificate = null; foreach (X509Certificate2 element in certificateCollection) { Console.WriteLine($"Found certificate: {element?.Thumbprint} {element?.Subject}; PrivateKey: {element?.HasPrivateKey}"); if (certificate == null && element.HasPrivateKey) { certificate = element; } else { element.Dispose(); } } if (certificate == null) { throw new FileNotFoundException($"{parameters.CertificatePath} did not contain any certificate with a private key."); } Console.WriteLine($"Using certificate {certificate.Thumbprint} {certificate.Subject}"); return certificate; }
在
SetupDeviceClientAsync
方法中,将case "dps"
的代码块替换为以下代码:case "dps": s_logger.LogDebug($"Initializing via DPS"); Console.WriteLine($"Loading the certificate..."); X509Certificate2 certificate = LoadProvisioningCertificate(parameters); DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, certificate, cancellationToken); var authMethod = new DeviceAuthenticationWithX509Certificate(dpsRegistrationResult.DeviceId, certificate); deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod); break;
将
ProvisionDeviceAsync
方法替换为以下代码:private static async Task<DeviceRegistrationResult> ProvisionDeviceAsync(Parameters parameters, X509Certificate2 certificate, CancellationToken cancellationToken) { SecurityProvider security = new SecurityProviderX509Certificate(certificate); ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt(); ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, security, mqttTransportHandler); var pnpPayload = new ProvisioningRegistrationAdditionalData { JsonData = PnpConvention.CreateDpsPayload(ModelId), }; return await pdc.RegisterAsync(pnpPayload, cancellationToken); }
保存更改。
若要运行该示例:
将以下环境变量添加到项目中:
IOTHUB_DEVICE_DPS_DEVICE_ID
:mytestselfcertprimaryIOTHUB_DEVICE_X509_CERT
:<full path to folder that contains PFX files>mytestselfcertprimary.pfx
IOTHUB_DEVICE_X509_PASSWORD
:1234。
生成并运行应用程序。 验证设备预配是否成功。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到包含 pom.xml 文件的 azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample 文件夹,以及用于温度控制器设备示例的 src 文件夹。
编辑 pom.xml 文件,在
<dependencies>
节点中添加以下依赖关系配置:<dependency> <groupId>com.microsoft.azure.sdk.iot.provisioning.security</groupId> <artifactId>${x509-provider-artifact-id}</artifactId> <version>${x509-provider-version}</version> </dependency>
保存更改。
在文本编辑器中打开 src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java 文件。
将
SecurityProviderSymmetricKey
import 语句替换为以下 import 语句:import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider; import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert; import com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException;
添加以下 import 语句:
import java.nio.file.*;
将
SecurityProviderException
添加到main
方法引发的异常列表中:public static void main(String[] args) throws IOException, URISyntaxException, ProvisioningDeviceClientException, InterruptedException, SecurityProviderException {
将
initializeAndProvisionDevice
方法替换为以下代码:private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, SecurityProviderException { String deviceX509Key = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_KEY")))); String deviceX509Cert = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_CERT")))); SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(deviceX509Cert, deviceX509Key, null); ProvisioningDeviceClient provisioningDeviceClient; ProvisioningStatus provisioningStatus = new ProvisioningStatus(); provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityProviderX509); AdditionalData additionalData = new AdditionalData(); additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID)); provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus, additionalData); while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) { if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR || provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED || provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED) { provisioningStatus.exception.printStackTrace(); System.out.println("Registration error, bailing out"); break; } System.out.println("Waiting for Provisioning Service to register"); Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION); } ClientOptions options = new ClientOptions(); options.setModelId(MODEL_ID); if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) { System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri()); System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId()); String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri(); String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId(); log.debug("Opening the device client."); deviceClient = DeviceClient.createFromSecurityProvider(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT, options); deviceClient.open(); } }
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
生成并运行应用程序。 验证设备预配是否成功。
还可以对“mytestselfcertsecondary”证书重复上述步骤。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到包含 pnp_temperature_controller.js 应用程序的 azure-iot-sdk-node/device/samples/javascript 文件夹,并运行以下命令以安装 X.509 包:
npm install azure-iot-security-x509 --save
在文本编辑器中打开 pnp_temperature_controller.js 文件。
编辑
require
语句以包含以下代码:const fs = require('fs'); const X509Security = require('azure-iot-security-x509').X509Security;
将以下四行代码添加到“DPS 连接信息”部分以初始化
deviceCert
变量:const deviceCert = { cert: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_CERT).toString(), key: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_KEY).toString() };
通过将第一行替换为以下代码,编辑用于创建客户端的
provisionDevice
函数:var provSecurityClient = new X509Security(registrationId, deviceCert);
在同一函数中,按如下所示修改设置
deviceConnectionString
变量的行:deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';x509=true';
在
main
函数中,在调用Client.fromConnectionString
的行后添加以下行:client.setOptions(deviceCert);
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
执行脚本,验证设备预配是否成功:
node pnp_temperature_controller.js
还可以对“mytestselfcertsecondary”证书重复上述步骤。
若要修改示例代码以使用 X.509 证书,请执行以下操作:
导航到 azure-iot-device/samples/pnp 文件夹,并在文本编辑器中打开 temp_controller_with_thermostats.py 文件。
添加以下
from
语句以导入 X.509 功能:from azure.iot.device import X509
按下面所示修改
provision_device
函数的第一个部分:async def provision_device(provisioning_host, id_scope, registration_id, x509, model_id): provisioning_device_client = ProvisioningDeviceClient.create_from_x509_certificate( provisioning_host=provisioning_host, registration_id=registration_id, id_scope=id_scope, x509=x509, )
在
main
函数中,将用于设置symmetric_key
变量的行替换为以下代码:x509 = X509( cert_file=os.getenv("IOTHUB_DEVICE_X509_CERT"), key_file=os.getenv("IOTHUB_DEVICE_X509_KEY"), )
在
main
函数中,将provision_device
函数调用替换为以下代码:registration_result = await provision_device( provisioning_host, id_scope, registration_id, x509, model_id )
在
main
函数中,将IoTHubDeviceClient.create_from_symmetric_key
函数调用替换为以下代码:device_client = IoTHubDeviceClient.create_from_x509_certificate( x509=x509, hostname=registration_result.registration_state.assigned_hub, device_id=registration_result.registration_state.device_id, product_info=model_id, )
保存更改。
若要运行该示例:
在 shell 环境中,添加以下两个环境变量。 请务必提供 PEM 文件的完整路径,并为操作系统使用正确的路径分隔符:
set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
提示
完成创建客户端应用程序并将其连接到 Azure IoT Central 应用程序教程后,可以设置其他所需的环境变量。
执行脚本,验证设备预配是否成功:
python temp_controller_with_thermostats.py
还可以对“mytestselfcertsecondary”证书重复上述步骤。
连接 IoT Edge 设备
本部分假设使用组注册来连接 IoT Edge 设备。 遵循前面部分中所述的步骤完成以下操作:
若要使用 X.509 设备证书将 IoT Edge 设备连接到 IoT Central,请执行以下操作:
将设备证书和密钥文件复制到 IoT Edge 设备。 在前面的组注册示例中,这些文件名为 sampleDevice01_key.pem 和 sampleDevice01_cert.pem。
在 IoT Edge 设备上,按下面所示编辑 /etc/aziot/config.toml 配置文件中的
provisioning
节:# DPS X.509 provisioning configuration provisioning: source: "dps" global_endpoint: "https://global.azure-devices-provisioning.net" scope_id: "<SCOPE_ID>" attestation: method: "x509" # registration_id: "<OPTIONAL REGISTRATION ID. LEAVE COMMENTED OUT TO REGISTER WITH CN OF identity_cert>" identity_cert: "file:///<path>/sampleDevice01_cert.pem" identity_pk: "file:///<path>/sampleDevice01_key.pem" # always_reprovision_on_startup: true # dynamic_reprovisioning: false [provisioning] source = "dps" global_endpoint = "https://global.azure-devices-provisioning.net" id_scope = "<SCOPE_ID>" [provisioning.attestation] method = "x509" registration_id = "env-sens-001" identity_pk = "file:///<path>/envSens001_key.pem" identity_cert = "file:///<path>/envSens001_cert.pem"
提示
无需为
registration_id
添加值。 IoT Edge 可以使用 X.509 证书中的 CN 值。运行以下命令重启 IoT Edge 运行时:
sudo iotedge config apply
有关详细信息,请参阅使用 X.509 证书在 Linux 上大规模创建和预配 IoT Edge 设备。
将下游设备连接到 IoT Edge
IoT Edge 使用 X.509 证书来保护下游设备与充当传输网关的 IoT Edge 设备之间的连接。 若要详细了解如何配置此方案,请参阅将下游设备连接到 Azure IoT Edge 网关。
滚动更新 X.509 设备证书
在 IoT Central 应用程序的生命周期内,可能需要滚动更新 X.509 证书。 例如:
- 如果出现安全违规,滚动更新证书是一种安全最佳做法,可帮助保护系统。
- X.509 证书具有过期日期。 滚动更新证书的频率取决于解决方案的安全需求。 解决方案涉及到高度敏感数据的客户可能每天都会滚动更新证书,而其他客户则每几年滚动更新一次证书。
IoT Central 允许配置主要和次要 X.509 证书,以确保连接不会中断。 如果主要证书和次要证书的过期日期不同,你可以滚动更新已过期的证书,而设备仍可使用另一个证书进行连接。
有关详细信息,请参阅“假定漏洞”方法。
本部分介绍如何在 IoT Central 中滚动更新证书。 在 IoT Central 中滚动更新证书时,还需要将新设备证书复制到设备。
获取新的 X.509 证书
从证书提供者获取新的 X.509 证书。 可以使用 OpenSSL 之类的工具创建自己的 X.509 证书。 此方法适合用于测试 X.509 证书,但提供的安全保障较低。 除非你已准备好充当自己的 CA 提供者,否则请仅将此方法用于测试。
注册组和安全违规
若要为了响应安全漏洞而更新组注册,应使用以下方法来立即更新当前证书。 如果主要证书和次要证书都已泄露,请针对这两个证书完成以下步骤:
导航到左侧窗格中的“访问权限”,然后选择“设备连接组”。
在注册组下的列表中选择组名称。
对于证书更新,选择“管理主要证书”或“管理次要证书”。
在注册组中添加并验证根 X.509 证书。
单独注册和安全违规
如果你是为了响应安全漏洞而滚动更新证书,请使用以下方法立即更新当前证书。 如果主要证书和次要证书都已泄露,请针对这两个证书完成以下步骤:
选择“设备”,然后选择设备。
选择“连接”,然后将连接方法选为“个人注册”
选择“证书(X.509)”作为机制。
对于证书更新,选择文件夹图标以选择注册条目的待上传新证书。 选择“保存”。
注册组和证书过期
若要处理证书过期问题,请使用以下方法立即更新当前证书:
导航到左侧窗格中的“访问权限”,然后选择“设备连接组”。
在注册组下的列表中选择组名称。
对于证书更新,选择“管理主要证书”。
在注册组中添加并验证根 X.509 证书。
以后当次要证书过期时,请回来更新次要证书。
单独注册和证书过期
如果滚动更新证书的目的是处理证书过期问题,则应按如下所述使用次要证书配置,以减少设备尝试在应用程序中进行预配时造成的停机时间。
当次要证书即将过期,因此需要滚动更新时,可以换用主要证书配置。 以这种方式在主要证书与次要证书之间轮换可以减少设备尝试在应用程序中进行预配时造成的停机时间。
选择“设备”,然后选择设备。
选择“连接”,然后将连接方法选为“个人注册”
选择“证书(X.509)”作为机制。
对于次要证书更新,选择文件夹图标以选择注册条目的待上传新证书。 选择“保存”。
以后当主要证书过期时,请回来并更新主要证书。