你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

如何在不使用 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 Cloud Shell,这是一个交互式 Shell,可在浏览器中运行 CLI 命令。 建议使用此选项,因为无需安装任何插件。 如果是首次使用 Cloud Shell,请登录到 Azure 门户。 按照 Cloud Shell 快速入门中的步骤启动 Cloud Shell 并选择 Bash 环境。
    • (可选)在本地计算机上运行 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
    

使用证书链

如果使用的是注册组,则必须使用证书链进行身份验证。 对于单独注册,可以使用证书链或自签名证书。

若要创建证书链,请按照创建 X.509 证书链中的说明进行操作。 对于本文,你只需要一台设备,因此可以在为第一个设备创建私钥和证书链后停止。

完成后,应会得到以下文件:

证书 文件 说明
根 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。

注意

如果使用 Cloud Shell 运行 Azure CLI 命令,则可以在运行命令之前使用上传按钮将证书文件上传到云驱动器。

Screenshot that shows the upload file button in Azure Cloud Shell.

使用注册组

若要创建用于本文的注册组,请使用 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。

注意

如果使用 Cloud Shell 运行 Azure CLI 命令,则可以在运行命令之前使用上传按钮将证书文件上传到云驱动器。

Screenshot that shows the upload file button in Azure Cloud Shell.

注意

如果需要,可以基于先前在 DPS 中上传并验证的签名证书创建注册组(请参阅下一部分)。 为此,请使用 --ca-name 指定证书名称,并在 az iot dps enrollment-group create 命令中省略 --certificate-path 参数。

上传并验证签名证书

如果将证书链用于单独注册或注册组,则必须将设备证书签名链中的至少一个证书上传到 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”。

注意

如果使用 Cloud Shell 运行 Azure CLI 命令,则可以在运行命令之前使用上传按钮将证书文件上传到云驱动器。

Screenshot that shows the upload file button in Azure Cloud Shell.

注意

本部分的步骤会在上传时自动验证证书。 你还可以手动验证证书。 有关详细信息,请参阅中间或根 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:分配失败。 响应的 registrationState 记录中将返回 errorCodeerrorMessage,以指示哪个操作失败。

  • 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.net",
      "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.net/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.net/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.net/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.net/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

后续步骤