如何在不使用 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 --versionpython3 --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 个字符。 如果在以下步骤中更改设备证书的使用者公用名称,请确保名称符合此格式。

使用自签名证书

若要创建用于单独注册的自签名证书,请导航到要在其中创建证书的目录并执行以下步骤:

  1. 运行以下命令:

    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) 提供额外的西文斜杠。

  2. 当系统要求“输入 PEM 通行短语:”时,请使用通行短语 1234

  3. 如果系统要求“验证 - 输入 PEM 通行短语:”,请再次使用通行短语 1234

    现在应在运行 openssl 命令的目录中生成公钥证书文件 (device-cert.pem) 和私钥文件 (device-key.pem)。

    证书文件的使用者公用名 (CN) 设置为 my-x509-device

    私钥文件受通行短语的保护:1234

  4. 证书文件采用 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) 相匹配。

    • 如果你已按照使用自签名证书中的说明进行操作,则注册 ID 为 my-x509-device。

    • 如果你已按照使用证书链中的说明进行操作,则注册 ID 为 device-01。

  • 证书路径是设备证书的路径。

    • 如果你已按照使用自签名证书中的说明进行操作,则文件名为 device-cert.pem。

    • 如果你已按照使用证书链中的说明进行操作,则文件名为 certs/device-01.cert.pem。

使用注册组

若要创建用于本文的注册组,请使用 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 在何处查找设备的私钥。

    • 如果你已按照使用自签名证书中的说明进行操作,则文件名为 device-key.pem,因此请使用 --key device-cert.pem:1234

    • 如果你已按照使用证书链中的说明进行操作,则密钥路径为 certs/device-01-full-chain.cert.pem,因此请使用 --cert certs/device-01-full-chain.cert.pem

  • -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 记录中将返回 errorMessageregistrationState,以指示哪个操作失败。

  • 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 属性为 assignedregistrationState.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

    • 如果使用的是自签名证书,则设备证书文件仅包含单个 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 在何处查找设备的私钥。

    • 如果你已按照使用自签名证书中的说明进行操作,则文件名为 device-key.pem,因此请使用 --key device-cert.pem:1234

    • 如果你已按照使用证书链中的说明进行操作,则密钥路径为 certs/device-01-full-chain.cert.pem,因此请使用 --cert certs/device-01-full-chain.cert.pem

  • -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

后续步骤