你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
如何在不使用 SDK 的情况下通过 HTTPS 使用对称密钥
在本操作指南文章中,你将在不使用 Azure IoT DPS 设备 SDK 的情况下,通过 HTTPS 使用对称密钥来预配设备。 大多数语言提供用于发送 HTTP 请求的库,但在本文中,你将使用 cURL 命令行工具通过 HTTPS 发送和接收请求,而不是专注于特定的语言。
可以在 Linux 或 Windows 计算机上执行本文中的步骤。 如果在适用于 Linux 的 Windows 子系统 (WSL) 上运行或者在 Linux 计算机上运行,可以在本地系统上的 Bash 提示符下输入所有命令。 如果在 Windows 上运行,请在本地系统上的 GitBash 提示符下输入所有命令。
本文有多个路径,具体取决于你选择使用的注册条目类型。 安装必备组件后,请务必先阅读概述,然后继续。
先决条件
如果没有 Azure 订阅,请在开始之前创建一个免费帐户。
完成通过 Azure 门户设置 IoT 中心设备预配服务中的步骤。
确保计算机上安装了 Python 3.7 或更高版本。 可以通过运行
python --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 提示符。
概述
创建单独注册或注册组条目后,继续创建 SAS 令牌并将设备注册到 DPS。
使用单独注册
如果你想要创建新的单独注册以用于本文,可以使用 az iot dps enrollment create 命令为对称密钥证明创建单独注册。
以下命令使用默认分配策略为 DPS 实例创建一个注册条目,并让 DPS 为设备分配主密钥和辅助密钥:
az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --attestation-type symmetrickey
替换资源组和 DPS 实例的名称。
注册 ID 是设备的注册 ID。 注册 ID 是一个不区分大小写的字符串(最大长度为 128 个字符),包含字母数字字符和以下特殊字符:
'-'
、'.'
、'_'
、':'
。 最后一个字符必须是字母数字或短划线 ('-'
)。 确保命令中使用的注册 ID 符合此格式。
分配的对称密钥在响应的 attestation 属性中返回:
{
"allocationPolicy": null,
"attestation": {
"symmetricKey": {
"primaryKey": "G3vn0IZH9oK3d4wsxFpWBtd2KUrtjI+39dZVRf26To8w9OX0LaFV9yZ93ELXY7voqHEUsNhnb9bt717UP87KxA==",
"secondaryKey": "4lNxgD3lUAOEOied5/xOocyiUSCAgS+4b9OvXLDi8ug46/CJzIn/3rN6Ys6gW8SMDDxMQDaMRnIoSd1HJ5qn/g=="
},
"tpm": null,
"type": "symmetricKey",
"x509": null
},
...
}
记下单独注册条目的主密钥和注册 ID,因此稍后在本文中需要用到。
如果你要为本文使用现有的单独注册,可以使用 az iot dps enrollment show 命令获取主密钥:
az iot dps enrollment show -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --show-keys true
使用注册组
如果你想要创建新的注册组以用于本文,可以使用 az iot dps enrollment-group create 命令为对称密钥证明创建注册组。
以下命令使用默认分配策略为 DPS 实例创建一个注册组条目,并让 DPS 为注册组分配主密钥和辅助密钥:
az iot dps enrollment-group create -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id}
替换资源组和 DPS 实例的名称。
注册 ID 是一个不区分大小写的字符串(最大长度为 128 个字符),包含字母数字字符和以下特殊字符:
'-'
、'.'
、'_'
、':'
。 最后一个字符必须是字母数字或短划线 ('-'
)。 它可以是你选择用于注册组的任何名称。
分配的对称密钥在响应的 attestation 属性中返回:
{
"allocationPolicy": null,
"attestation": {
"symmetricKey": {
"primaryKey": "G3vn0IZH9oK3d4wsxFpWBtd2KUrtjI+39dZVRf26To8w9OX0LaFV9yZ93ELXY7voqHEUsNhnb9bt717UP87KxA==",
"secondaryKey": "4lNxgD3lUAOEOied5/xOocyiUSCAgS+4b9OvXLDi8ug46/CJzIn/3rN6Ys6gW8SMDDxMQDaMRnIoSd1HJ5qn/g=="
},
"tpm": null,
"type": "symmetricKey",
"x509": null
},
...
}
记下主密钥。
如果你要为本文使用现有的单独注册,可以使用 az iot dps enrollment-group show 命令获取主密钥:
az iot dps enrollment-group show -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --show-keys true
派生一个设备密钥
在组注册中使用对称密钥证明时,不会直接使用注册组密钥。 你需要从注册组密钥为每个设备派生唯一密钥。 有关详细信息,请参阅采用对称密钥的组注册。
在本部分,你将从注册组主密钥生成设备密钥,以计算设备的唯一注册 ID 的 HMAC-SHA256。 然后,结果将转换为 Base64 格式。
使用 openssl 生成唯一密钥。 你将使用以下 Bash shell 脚本。 请将
{primary-key}
替换为前面复制的注册组主密钥,并将{contoso-simdevice}
替换为要用于设备的注册 ID。 注册 ID 是一个不区分大小写的字符串(最大长度为 128 个字符),包含字母数字字符和以下特殊字符:'-'
、'.'
、'_'
、':'
。 最后一个字符必须是字母数字或短划线 ('-'
)。KEY={primary-key} REG_ID={contoso-simdevice} keybytes=$(echo $KEY | base64 --decode | xxd -p -u -c 1000) echo -n $REG_ID | openssl sha256 -mac HMAC -macopt hexkey:$keybytes -binary | base64
该脚本将输出类似于如下密钥:
p3w2DQr9WqEGBLUSlFi1jPQ7UWQL4siAGy75HFTFbf8=
记下派生的设备密钥和用于生成该密钥的注册 ID,因为在下一部分需要用到。
还可以使用 Azure CLI 或 PowerShell 来派生设备密钥。 有关详细信息,请参阅派生设备密钥。
创建 SAS 令牌
使用对称密钥证明时,设备将使用共享访问签名 (SAS) 令牌对 DPS 进行身份验证。 对于通过单独注册进行的设备预配,该令牌是使用注册条目中设置的主密钥或辅助密钥签名的。 对于通过注册组进行的设备预配,该令牌是使用派生的设备密钥签名的,而该密钥是使用注册组条目中设置的主密钥或辅助密钥生成的。 该令牌指定过期时间和目标资源 URI。
可以使用以下 Python 脚本生成 SAS 令牌:
from base64 import b64encode, b64decode
from hashlib import sha256
from time import time
from urllib.parse import quote_plus, urlencode
from hmac import HMAC
def generate_sas_token(uri, key, policy_name, expiry=3600):
ttl = time() + expiry
sign_key = "%s\n%d" % ((quote_plus(uri)), int(ttl))
print(sign_key)
signature = b64encode(HMAC(b64decode(key), sign_key.encode('utf-8'), sha256).digest())
rawtoken = {
'sr' : uri,
'sig': signature,
'se' : str(int(ttl))
}
if policy_name is not None:
rawtoken['skn'] = policy_name
return 'SharedAccessSignature ' + urlencode(rawtoken)
uri = '[resource_uri]'
key = '[device_key]'
expiry = [expiry_in_seconds]
policy= '[policy]'
print(generate_sas_token(uri, key, policy, expiry))
其中:
[resource_uri]
是你尝试使用此令牌访问的资源的 URI。 对于 DPS,其格式为[dps_id_scope]/registrations/[dps_registration_id]
,其中[dps_id_scope]
是 DPS 实例的 ID 范围,[dps_registration_id]
是用于设备的注册 ID。可以从 Azure 门户中实例的“概述”窗格获取 DPS 实例的 ID 范围,或者可以使用 az iot dps show Azure CLI 命令(请将占位符替换为资源组和 DPS 实例的名称):
az iot dps show -g {resource_group_name} --name {dps_name}
[device_key]
是与设备关联的设备密钥。 此密钥是在单独注册中为你指定或自动生成的密钥,或者是组注册的派生密钥。[expiry_in_seconds]
是此 SAS 令牌的有效期(以秒为单位)。[policy]
是与设备密钥关联的策略。 对于 DPS 设备注册,策略将硬编码为“注册”。
名为 my-symkey-device
且有效期为 30 天的设备的一组示例输入如下所示。
uri = '0ne00111111/registrations/my-symkey-device'
key = '18RQk/hOPJR9EbsJlk2j8WA6vWaj/yi+oaYg7zmxfQNdOyMSu+SJ8O7TSlZhDJCYmn4rzEiVKIzNiVAWjLxrGA=='
expiry = 2592000
policy='registration'
修改设备和 DPS 实例的脚本并将其保存为 Python 文件;例如,generate_token.py。 运行脚本,例如 python generate_token.py
。 它应输出如下所示的 SAS 令牌:
0ne00111111%2Fregistrations%2Fmy-symkey-device
1663952627
SharedAccessSignature sr=0ne00111111%2Fregistrations%2Fmy-symkey-device&sig=eNwg52xQdFTNf7bgPAlAJBCIcONivq%2Fck1lf3wtxI4A%3D&se=1663952627&skn=registration
复制并保存以 SharedAccessSignature
开头的整个行。 此行是 SAS 令牌。 在后续部分需要使用它。
若要详细了解如何将 SAS 令牌与 DPS 配合使用以及令牌的结构,请参阅使用 SAS 控制对 DPS 的访问。
注册设备
调用注册设备 REST API 以通过 DPS 预配设备。
使用以下 curl 命令:
curl -L -i -X PUT -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: [sas_token]' -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 调用是必需的。-H 'Content-Type: application/json'
告知 DPS 我们正在发布 JSON 内容,其值必须是“application/json”。-H 'Content-Encoding: utf-8'
告知 DPS 我们对消息正文使用的编码。 请根据你的操作系统/客户端设置适当的值;但是,值通常是utf-8
。-H 'Authorization: [sas_token]'
告知 DPS 使用你的 SAS 令牌进行身份验证。 请将 [sas_token] 替换为在创建 SAS 令牌中生成的令牌。-d '{"registrationId": "[registration_id]"}'
,–d
参数是发布的消息的“数据”或正文。 它必须是 JSON,格式为 '{"registrationId":"[registration_id"}'。 请注意,对于 curl,需要将它括在单引号中;否则需要转义 JSON 中的双引号。最后,最后一个参数是要发布到的 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 -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: SharedAccessSignature sr=0ne00111111%2Fregistrations%2Fmy-symkey-device&sig=eNwg52xQdFTNf7bgPAlAJBCIcONivq%2Fck1lf3wtxI4A%3D&se=1663952627&skn=registration' -d '{"registrationId": "my-symkey-device"}' https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-symkey-device/register?api-version=2021-06-01
成功的调用将返回类似于下面的响应:
HTTP/1.1 202 Accepted
Date: Wed, 31 Aug 2022 22:02:49 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Location: https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-symkey-device/register
Retry-After: 3
x-ms-request-id: a021814f-0cf6-4ce9-a1e9-ead7eb5118d9
Strict-Transport-Security: max-age=31536000; includeSubDomains
{"operationId":"5.316aac5bdc130deb.b1e02da8-c3a0-4ff2-a121-7ea7a6b7f550","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 -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: [sas_token]' https://global.azure-devices-provisioning.net/[dps_id_scope]/registrations/[registration_id]/operations/[operation_id]?api-version=2019-03-31
使用在“注册设备”请求中所用的同一 ID 范围、注册 ID 和 SAS 令牌。 使用“注册设备”响应中返回的操作 ID。
例如:
curl -L -i -X GET -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: SharedAccessSignature sr=0ne00111111%2Fregistrations%2Fmy-symkey-device&sig=eNwg52xQdFTNf7bgPAlAJBCIcONivq%2Fck1lf3wtxI4A%3D&se=1663952627&skn=registration' https://global.azure-devices-provisioning.net/0ne00111111/registrations/my-symkey-device/operations/5.316aac5bdc130deb.f4f1828c-4dab-4ca9-98b2-dfc63b5835d6?api-version=2021-06-01
以下输出显示了对已成功分配的设备返回的响应。 请注意 status
属性为 assigned
,registrationState.assignedHub
属性设置为在其中预配了设备的 IoT 中心。
HTTP/1.1 200 OK
Date: Wed, 31 Aug 2022 22:05:23 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
x-ms-request-id: ffb98d42-023e-4e75-afb0-1807ff091cbb
Strict-Transport-Security: max-age=31536000; includeSubDomains
{
"operationId":"5.316aac5bdc130deb.b1e02da8-c3a0-4ff2-a121-7ea7a6b7f550",
"status":"assigned",
"registrationState":{
"registrationId":"my-symkey-device",
"createdDateTimeUtc":"2022-08-31T22:02:50.5163352Z",
"assignedHub":"MyExampleHub.azure-devices.net",
"deviceId":"my-symkey-device",
"status":"assigned",
"substatus":"initialAssignment",
"lastUpdatedDateTimeUtc":"2022-08-31T22:02:50.7370676Z",
"etag":"IjY5MDAzNTUyLTAwMDAtMDMwMC0wMDAwLTYzMGZkYThhMDAwMCI="
}
}
发送遥测消息
在发送遥测消息之前,需要为设备分配到的 IoT 中心创建 SAS 令牌。 使用为 DPS 实例的 SAS 令牌签名时所用的相同主密钥或派生设备密钥来为此令牌签名。
为 IoT 中心创建 SAS 令牌
若要创建 SAS 令牌,可以运行为 DPS 实例创建令牌时使用的代码,不过需要做出以下更改:
uri = '[resource_uri]'
key = '[device_key]'
expiry = [expiry_in_seconds]
policy= None
其中:
[resource_uri]
是你尝试使用此令牌访问的资源的 URI。 对于向 IoT 中心发送消息的设备,其格式为[iot-hub-host-name]/devices/[device-id]
。对于
[iot-hub-host-name]
,请使用在上一部分所述的assignedHub
属性中返回的 IoT 中心主机名。对于
[device-id]
,请使用在上一部分所述的deviceId
属性中返回的设备 ID。
[device_key]
是与设备关联的设备密钥。 此密钥是在单独注册中为你指定或自动生成的密钥,或者是组注册的派生密钥。 (它与前面用来为 DPS 创建令牌的密钥相同。)[expiry_in_seconds]
是此 SAS 令牌的有效期(以秒为单位)。policy=None
:向 IoT 中心发送遥测数据的设备不需要策略,因此该参数设置为None
。
向名为 my-symkey-device
的 IoT 中心发送遥测数据的、其令牌有效期为一小时的名为 MyExampleHub
的设备的一组示例输入如下所示:
uri = 'MyExampleHub.azure-devices.net/devices/my-symkey-device'
key = '18RQk/hOPJR9EbsJlk2j8WA6vWaj/yi+oaYg7zmxfQNdOyMSu+SJ8O7TSlZhDJCYmn4rzEiVKIzNiVAWjLxrGA=='
expiry = 3600
policy= None
以下输出显示了这些输入的示例 SAS 令牌:
SharedAccessSignature sr=MyExampleHub.azure-devices.net%2Fdevices%2Fmy-symkey-device&sig=f%2BwW8XOKeJOtiPc9Iwjc4OpExvPM7NlhM9qxN2a1aAM%3D&se=1663119026
若要详细了解如何为 IoT 中心创建 SAS 令牌,包括其他编程语言中的示例代码,请参阅使用共享访问签名控制对 IoT 中心的访问。
注意
为方便起见,可以使用 Azure CLI az IoT hub generate-sas-token 命令获取已注册到 IoT 中心的设备的 SAS 令牌。 例如,以下命令生成持续时间为一小时的 SAS 令牌。 对于 {iothub_name}
,只需获取主机名的第一个组成部分,例如 MyExampleHub
。
az iot hub generate-sas-token -d {device_id} -n {iothub_name}
将数据发送到 IoT 中心
调用 IoT 中心发送设备事件 REST API 将遥测数据发送到设备。
使用以下 curl 命令:
curl -L -i -X POST -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: [sas_token]' -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 调用是必需的。-H 'Content-Type: application/json'
告知 IoT 中心我们正在发布 JSON 内容,其值必须是“application/json”。-H 'Content-Encoding: utf-8'
告知 IoT 中心我们对消息正文使用的编码。 请根据你的操作系统/客户端设置适当的值;但是,值通常是utf-8
。-H 'Authorization: [sas_token]'
告知 IoT 中心使用你的 SAS 令牌进行身份验证。 请将[sas_token]
替换为针对分配的 IoT 中心生成的令牌。-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。
例如,对于设备 ID 为 my-symkey-device
的、将遥测数据点发送到名为 MyExampleHub
的 IoT 中心的设备:
curl -L -i -X POST -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -H 'Authorization: SharedAccessSignature sr=MyExampleHub.azure-devices.net%2Fdevices%2Fmy-symkey-device&sig=f%2BwW8XOKeJOtiPc9Iwjc4OpExvPM7NlhM9qxN2a1aAM%3D&se=1663119026' -d '{"temperature": 30}' https://MyExampleHub.azure-devices.net/devices/my-symkey-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: 9e278582-3561-417b-b807-76426195920f
Date: Wed, 14 Sep 2022 00:32:53 GMT
后续步骤
若要详细了解对称密钥证明,请参阅对称密钥证明。
若要详细了解 SAS 令牌及其结构,请参阅使用 SAS 控制对 DPS 的访问。