如何在不使用 SDK 的情况下通过 HTTPS 使用 X.509 证书
在本操作指南文章中,你将在不使用 Azure IoT DPS 设备 SDK 的情况下,通过 HTTPS 使用 X.509 证书来预配设备。 大多数语言提供用于发送 HTTP 请求的库,但在本文中,你将使用 cURL 命令行工具通过 HTTPS 发送和接收请求,而不是专注于特定的语言。
可以在 Linux 或 Windows 计算机上执行本文中的步骤。 如果在适用于 Linux 的 Windows 子系统 (WSL) 上运行或者在 Linux 计算机上运行,可以在本地系统上的 Bash 提示符下输入所有命令。 如果在 Windows 上运行,请在本地系统上的 GitBash 提示符下输入所有命令。
本文有多个路径,具体取决于你选择使用的注册条目类型和 X.509 证书。 安装必备组件后,请务必先阅读概述,然后继续。
先决条件
如果没有 Azure 订阅,请在开始前创建一个试用版订阅。
完成通过 Azure 门户设置 IoT 中心设备预配服务中的步骤。
确保已在计算机上安装 Python 3.7 或更高版本。 可以通过运行
python --version
或python3 --version
来检查 Python 版本。如果在 Windows 中运行,请安装最新版本的 Git。 确保将 Git 添加到可供命令窗口访问的环境变量。 请参阅软件自由保护组织提供的 Git 客户端工具,了解要安装的最新版
git
工具,其中包括 Git Bash,这是一个命令行应用,可以用来与本地 Git 存储库交互。 在 Windows 上,请在本地系统上的 GitBash 提示符下输入所有命令。Azure CLI。 在本文中,有两个选项可用于运行 Azure CLI 命令:
使用 Azure Power Shell。
(可选)在本地计算机上运行 Azure CLI。 如果已安装 Azure CLI,请运行
az upgrade
以将 CLI 和扩展升级到当前版本。 要安装 Azure CLI,请参阅安装 Azure CLI。
如果在 Linux 或 WSL 环境中运行,请打开 Bash 提示符以在本地运行命令。 如果在 Windows 环境中运行,请打开 GitBash 提示符。
概述
本文介绍三种方案,对每种方案执行的初始步骤不同。 如果要:
创建设备证书
在本文中,你将使用 X.509 证书通过单独注册或注册组向 DPS 进行身份验证。
如果使用的是单独注册,则可以使用自签名 X.509 证书或由设备证书加上一个或多个签名证书组成的证书链。 如果使用的是注册组,则必须使用证书链。
重要
对于 X.509 注册身份验证,设备证书的使用者公用名称 (CN) 将用作设备的注册 ID。 注册 ID 是一个不区分大小写的字符串,包含字母数字字符和以下特殊字符:'-'
、'.'
、'_'
、':'
。 最后一个字符必须是字母数字或短划线 ('-'
)。 DPS 支持最大长度为 128 个字符的注册 ID;但是,X.509 证书的使用者公用名称限制为 64 个字符。 如果在以下步骤中更改设备证书的使用者公用名称,请确保名称符合此格式。
使用自签名证书
若要创建用于单独注册的自签名证书,请导航到要在其中创建证书的目录并执行以下步骤:
运行以下命令:
winpty openssl req -outform PEM -x509 -sha256 -newkey rsa:4096 -keyout device-key.pem -out device-cert.pem -days 30 -extensions usr_cert -addext extendedKeyUsage=clientAuth -subj "//CN=my-x509-device"
重要
仅当需要在 Windows 平台上使用 Git 来转义字符串时,才需要为使用者名称 (
//CN=my-x509-device
) 提供额外的西文斜杠。当系统要求“输入 PEM 通行短语:”时,请使用通行短语
1234
。如果系统要求“验证 - 输入 PEM 通行短语:”,请再次使用通行短语
1234
。现在应在运行
openssl
命令的目录中生成公钥证书文件 (device-cert.pem) 和私钥文件 (device-key.pem)。证书文件的使用者公用名 (CN) 设置为
my-x509-device
。私钥文件受通行短语的保护:
1234
。证书文件采用 Base64 编码。 若要查看证书文件的使用者公用名称 (CN) 和其他属性,请输入以下命令:
winpty openssl x509 -in device-cert.pem -text -noout
Certificate: Data: Version: 3 (0x2) Serial Number: 77:3e:1d:e4:7e:c8:40:14:08:c6:09:75:50:9c:1a:35:6e:19:52:e2 Signature Algorithm: sha256WithRSAEncryption Issuer: CN = my-x509-device Validity Not Before: May 5 21:41:42 2022 GMT Not After : Jun 4 21:41:42 2022 GMT Subject: CN = my-x509-device Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) Modulus: 00:d2:94:37:d6:1b:f7:43:b4:21:c6:08:1a:d6:d7: e6:40:44:4e:4d:24:41:6c:3e:8c:b2:2c:b0:23:29: ... 23:6e:58:76:45:18:03:dc:2e:9d:3f:ac:a3:5c:1f: 9f:66:b0:05:d5:1c:fe:69:de:a9:09:13:28:c6:85: 0e:cd:53 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 63:C0:B5:93:BF:29:F8:57:F8:F9:26:44:70:6F:9B:A4:C7:E3:75:18 X509v3 Authority Key Identifier: keyid:63:C0:B5:93:BF:29:F8:57:F8:F9:26:44:70:6F:9B:A4:C7:E3:75:18 X509v3 Extended Key Usage: TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption 82:8a:98:f8:47:00:85:be:21:15:64:b9:22:b0:13:cc:9e:9a: ed:f5:93:b9:4b:57:0f:79:85:9d:89:47:69:95:65:5e:b3:b1: ... cc:b2:20:9a:b7:f2:5e:6b:81:a1:04:93:e9:2b:92:62:e0:1c: ac:d2:49:b9:36:d2:b0:21
使用证书链
如果使用的是注册组,则必须使用证书链进行身份验证。 对于单独注册,可以使用证书链或自签名证书。
完成后,应会得到以下文件:
证书 | 文件 | 说明 |
---|---|---|
根 CA 证书。 | certs/azure-iot-test-only.root.ca.cert.pem | 将上传到 DPS 并验证。 |
中间 CA 证书 | certs/azure-iot-test-only.intermediate.cert.pem | 将用于在 DPS 中创建注册组。 |
device-01 私钥 | private/device-01.key.pem | 由设备用于在向 DPS 进行身份验证期间验证设备证书的所有权。 |
device-01 证书 | certs/device-01.cert.pem | 用于通过 DPS 创建单独注册条目。 |
device-01 完整链证书 | certs/device-01-full-chain.cert.pem | 由设备提供,用于向 DPS 进行身份验证和注册。 |
使用单独注册
若要创建用于本文的单独注册,请使用 az iot dps enrollment create 命令。
以下命令使用指定的设备证书为 DPS 实例创建一个使用默认分配策略的单独注册条目。
az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --attestation-type x509 --certificate-path {path to your certificate}
替换资源组和 DPS 实例的名称。
注册 ID 是设备的注册 ID,对于 X.509 注册,此 ID 必须与设备证书的使用者公用名称 (CN) 相匹配。
证书路径是设备证书的路径。
使用注册组
若要创建用于本文的注册组,请使用 az iot dps enrollment-group create 命令。
以下命令使用中间 CA 证书为 DPS 实例创建一个使用默认分配策略的注册组条目:
az iot dps enrollment-group create -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --certificate-path {path_to_your_certificate}
替换资源组和 DPS 实例的名称。
注册 ID 是一个不区分大小写的字符串,包含字母数字字符和以下特殊字符:
'-'
、'.'
、'_'
、':'
。 最后一个字符必须是字母数字或短划线 ('-'
)。 它可以是你选择用于注册组的任何名称。证书路径是中间证书的路径。 如果你已按照使用证书链中的说明进行操作,则文件名为 certs/azure-iot-test-only.intermediate.cert.pem。
注意
如果需要,可以基于先前在 DPS 中上传并验证的签名证书创建注册组(请参阅下一部分)。 为此,请使用 --ca-name
指定证书名称,并在 --certificate-path
命令中省略 az iot dps enrollment-group create
参数。
上传并验证签名证书
如果将证书链用于单独注册或注册组,则必须将设备证书签名链中的至少一个证书上传到 DPS 并对其进行验证。
对于单独注册,这可以是设备证书链中的任何签名证书。
对于注册组,这可以是在注册组中设置的证书,或其签名链中的任何证书,可上传的最高级别证书为根 CA 证书。
若要上传并验证证书,请使用 az iot dps certificate create 命令:
az iot dps certificate create -g {resource_group_name} --dps-name {dps_name} --certificate-name {friendly_name_for_your_certificate} --path {path_to_your_certificate} --verified true
替换资源组和 DPS 实例的名称。
证书路径是签名证书的路径。 对于本文,建议上传根 CA 证书。 如果你已按照使用证书链中的说明进行操作,则文件名为 certs/azure-iot-test-only.root.ca.cert.pem。
证书名称只能包含字母数字字符或以下特殊字符:
-._
。 不允许包含空格。 例如“azure-iot-test-only-root”。
注意
本部分的步骤会在上传时自动验证证书。 你还可以手动验证证书。 有关详细信息,请参阅中间或根 CA 的手动验证。
注册设备
调用注册设备 REST API 以通过 DPS 预配设备。
使用以下 curl 命令:
curl -L -i -X PUT --cert [path_to_your_device_cert] --key [path_to_your_device_private_key] -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"registrationId": "[registration_id]"}' https://global.azure-devices-provisioning.net/[dps_id_scope]/registrations/[registration_id]/register?api-version=2019-03-31
其中:
-L
告知 curl 遵循 HTTP 重定向。-i
告知 curl 在输出中包含协议标头。 这些标头并非绝对必要,但它们可能很有用。-X PUT
告知 curl 这是一个 HTTP PUT 命令。 对于此 API 调用是必需的。--cert [path_to_your_device_cert]
告知 curl 在何处查找设备的 X.509 证书。 如果设备私钥受通行短语保护,你可以在证书路径后面添加一个冒号,然后再添加该通行短语,例如:--cert my-device.pem:1234
。如果使用的是自签名证书,则设备证书文件仅包含单个 X.509 证书。 如果你已按照使用自签名证书中的说明进行操作,则文件名为 device-cert.pem,私钥通行短语为
1234
,因此请使用--cert device-cert.pem:1234
。如果使用证书链(例如,在通过注册组进行身份验证时),设备证书文件必须包含有效的证书链。 该证书链必须包括设备证书和任何签名证书,最高证书级别为已验证的证书。 如果你已按照使用证书链中的说明创建证书链,则文件路径为 certs/device-01-full-chain.cert.pem,因此请使用
--cert certs/device-01-full-chain.cert.pem
。
--key [path_to_your_device_private_key]
告知 curl 在何处查找设备的私钥。-H 'Content-Type: application/json'
告知 DPS 我们正在发布 JSON 内容,其值必须是“application/json”-H 'Content-Encoding: utf-8'
告知 DPS 我们对消息正文使用的编码。 请根据你的操作系统/客户端设置适当的值;但是,值通常是utf-8
。-d '{"registrationId": "[registration_id]"}'
,-d
参数是发布的消息的“数据”或正文。 它必须是 JSON,格式为 '{"registrationId":"[registration_id"}'。 请注意,对于 curl,需要将它括在单引号中;否则需要转义 JSON 中的双引号。 对于 X.509 注册,注册 ID 是设备证书的使用者公用名称 (CN)。最后,最后一个参数是要发布到的 URL。 对于“常规”(即非本地)DPS,将使用全局 DPS 终结点 global.azure-devices-provisioning.net:
https://global.azure-devices-provisioning.net/[dps_id_scope]/registrations/[registration_id]/register?api-version=2019-03-31
。 请注意,必须将[dps_scope_id]
和[registration_id]
替换为适当的值。
例如:
如果你已按照使用自签名证书中的说明进行操作:
curl -L -i -X PUT --cert device-cert.pem:1234 --key device-key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"registrationId": "my-x509-device"}' https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-x509-device/register?api-version=2021-06-01
如果你已按照使用证书链中的说明进行操作:
curl -L -i -X PUT --cert certs/device-01-full-chain.cert.pem --key private/device-01.key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"registrationId": "device-01"}' https://global.azure-devices-provisioning.net/0ne00111111/registrations/device-01/register?api-version=2021-06-01
成功的调用将返回类似于下面的响应:
HTTP/1.1 202 Accepted
Date: Sat, 27 Aug 2022 17:53:18 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Location: https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-x509-device/register
Retry-After: 3
x-ms-request-id: 05cdec07-c0c7-48f3-b3cd-30cfe27cbe57
Strict-Transport-Security: max-age=31536000; includeSubDomains
{"operationId":"5.506603669bd3e2bf.b3602f8f-76fe-4341-9214-bb6cfb891b8a","status":"assigning"}
响应包含操作 ID 和状态。 在本例中,状态设置为 assigning
。 DPS 注册可能是一个长时间运行的操作,因此它以异步方式完成。 通常,你会使用操作状态查找 REST API 轮询状态,以确定何时分配了设备或是否发生了故障。
DPS 的有效状态值为:
assigned
:状态调用的返回值将指示设备已分配到哪个 IoT 中心。assigning
:操作仍在运行。disabled
:DPS 中已禁用注册记录,因此无法分配设备。failed
:分配失败。 响应的errorCode
记录中将返回errorMessage
和registrationState
,以指示哪个操作失败。unassigned
若要调用“操作状态查找”API,请使用以下 curl 命令:
curl -L -i -X GET --cert [path_to_your_device_cert] --key [path_to_your_device_private_key] -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' https://global.azure-devices-provisioning.net/[dps_id_scope]/registrations/[registration_id]/operations/[operation_id]?api-version=2019-03-31
使用在“注册设备”请求中所用的同一 ID 范围、注册 ID 以及证书和密钥。 使用“注册设备”响应中返回的操作 ID。
例如,以下命令针对在使用自签名证书中创建的自签名证书。 (需要修改 ID 范围和操作 ID。)
curl -L -i -X GET --cert ./device-certPUT --cert device-cert.pem:1234 --key device-key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-x509-device/operations/5.506603669bd3e2bf.b3602f8f-76fe-4341-9214-bb6cfb891b8a?api-version=2021-06-01
以下输出显示了对已成功分配的设备返回的响应。 请注意 status
属性为 assigned
,registrationState.assignedHub
属性设置为在其中预配了设备的 IoT 中心。
HTTP/1.1 200 OK
Date: Sat, 27 Aug 2022 18:10:49 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
x-ms-request-id: 8f211bc5-3ed8-4c8b-9a79-e003e756e9e4
Strict-Transport-Security: max-age=31536000; includeSubDomains
{
"operationId":"5.506603669bd3e2bf.b3602f8f-76fe-4341-9214-bb6cfb891b8a",
"status":"assigned",
"registrationState":{
"x509":{
},
"registrationId":"my-x509-device",
"createdDateTimeUtc":"2022-08-27T17:53:19.5143497Z",
"assignedHub":"MyExampleHub.azure-devices.cn",
"deviceId":"my-x509-device",
"status":"assigned",
"substatus":"initialAssignment",
"lastUpdatedDateTimeUtc":"2022-08-27T17:53:19.7519141Z",
"etag":"IjEyMDA4NmYyLTAwMDAtMDMwMC0wMDAwLTYzMGE1YTBmMDAwMCI="
}
}
记下设备 ID 和分配的 IoT 中心。 在下一部分,你将使用它们来发送遥测消息。
发送遥测消息
调用 IoT 中心发送设备事件 REST API 将遥测数据发送到设备。
使用以下 curl 命令:
curl -L -i -X POST --cert [path_to_your_device_cert] --key [path_to_your_device_private_key] -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"temperature": 30}' https://[assigned_iot_hub_name].azure-devices.cn/devices/[device_id]/messages/events?api-version=2020-03-13
其中:
-X POST
告知 curl 这是一个 HTTP POST 命令。 对于此 API 调用是必需的。--cert [path_to_your_device_cert]
告知 curl 在何处查找设备的 X.509 证书。 如果设备私钥受通行短语保护,你可以在证书路径后面添加一个冒号,然后再添加该通行短语,例如:--cert my-device.pem:1234
。--key [path_to_your_device_private_key]
告知 curl 在何处查找设备的私钥。-H 'Content-Type: application/json'
告知 IoT 中心我们正在发布 JSON 内容,其值必须是“application/json”。-H 'Content-Encoding: utf-8'
告知 IoT 中心我们对消息正文使用的编码。 请根据你的操作系统/客户端设置适当的值;但是,值通常是utf-8
。-d '{"temperature": 30}'
,-d
参数是发布的消息的“数据”或正文。 对于本文,我们将发布单个温度数据点。 内容类型指定为 application/json,因此对于此请求,正文为 JSON。 请注意,对于 curl,需要将它括在单引号中;否则需要转义 JSON 中的双引号。最后一个参数是要发布到的 URL。 对于“发送设备事件”API,该 URL 是:
https://[assigned_iot_hub_name].azure-devices.cn/devices/[device_id]/messages/events?api-version=2020-03-13
。请将
[assigned_iot_hub_name]
替换为设备分配到的 IoT 中心的名称。请将
[device_id]
替换为注册设备时分配的设备 ID。 对于通过注册组预配的设备,设备 ID 将是注册 ID。 对于单独注册,可以选择性地指定一个与注册条目中的注册 ID 不同的设备 ID。
例如:
如果你已按照使用自签名证书中的说明进行操作:
curl -L -i -X POST --cert device-cert.pem:1234 --key device-key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"temperature": 30}' https://MyExampleHub.azure-devices.cn/devices/my-x509-device/messages/events?api-version=2020-03-13
如果你已按照使用证书链中的说明进行操作:
curl -L -i -X POST --cert certs/device-01-full-chain.cert.pem --key private/device-01.key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"temperature": 30}' https://MyExampleHub.azure-devices.cn/devices/my-x509-device/messages/events?api-version=2020-03-13
成功的调用将返回类似于下面的响应:
HTTP/1.1 204 No Content
Content-Length: 0
Vary: Origin
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: aa58c075-20d9-4565-8058-de6dc8524f14
Date: Wed, 31 Aug 2022 18:34:44 GMT
后续步骤
若要详细了解如何使用 X.509 证书进行证明,请参阅 X.509 证书证明。
若要详细了解如何上传和验证 X.509 证书,请参阅配置已验证的 CA 证书。