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

使用共享访问签名和安全令牌控制对 Azure IoT 中心设备预配服务(DPS)的访问

本文介绍用于保护 Azure IoT 中心设备预配服务(DPS)的可用选项。 预配服务使用 身份验证权限 授予对每个终结点的访问权限。 权限允许身份验证过程根据功能限制对服务实例的访问。

本文讨论:

  • 由预配服务用来验证服务和设备 REST API 的权限的身份验证过程和令牌。

  • 可以授予后端应用访问服务 API 的不同权限。

Authentication

设备 API 支持基于密钥的设备身份验证和基于 X.509 证书的设备身份验证。

服务 API 支持后端应用的基于密钥的身份验证。

使用基于密钥的身份验证时,设备预配服务使用安全令牌对服务进行身份验证,以避免在网络上发送密钥。 此外,安全令牌在时间有效性和范围方面受到限制。 Azure IoT 中心设备预配 SDK 自动生成令牌,而无需任何特殊配置。

在某些情况下,可能需要直接使用 HTTP 设备预配服务 REST API,而无需使用 SDK。 以下部分介绍如何直接针对 REST API 进行身份验证。

设备 API 身份验证

设备使用设备 API 向设备预配服务证明身份和接收 IoT 中心连接。

注释

若要接收经过身份验证的连接,必须先通过注册在设备预配服务中注册设备。 使用服务 API 以编程方式通过注册来注册设备。

设备必须在预配过程中向设备 API 进行身份验证。 在设置注册组或单个注册时,会定义设备用于身份验证的方法。 无论哪种身份验证方法,设备都必须向以下 URL 发出 HTTPS PUT 请求来配置自身。

    https://global.azure-devices-provisioning.net/[ID_Scope]/registrations/[registration_id]/register?api-version=2021-06-01

如果使用基于密钥的身份验证,则采用以下格式的 HTTP 授权 请求标头传递安全令牌:

    SharedAccessSignature sig={signature}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI} 

基于密钥的身份验证的安全令牌结构

安全令牌采用以下格式以 HTTP 授权 请求标头传递:

    SharedAccessSignature sig={signature}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI} 

预期值为:

价值 Description
{signature} 表单的 HMAC-SHA256 签名字符串: {URL-encoded-resourceURI} + "\n" + expiry。  重要说明:密钥是从 base64 解码的,用作执行 HMAC-SHA256 计算的键。
{expiry} 从纪元 1970 年 1 月 1日 00:00:00 UTC 时间至今秒数的 UTF8 字符串。
{URL-encoded-resourceURI} {ID_Scope}/registrations/{registration_id} 的小写 URL 编码
{policyName} 对于设备 API,此策略始终为“注册”。

以下 Python 代码片段展示了一个名为generate_sas_token的函数,该函数使用对称密钥身份验证类型,从输入urikeypolicy_nameexpiry中计算出用于单个注册的令牌。


from base64 import b64encode, b64decode, encode 
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)) 
    signature = b64encode(HMAC(b64decode(key), sign_key.encode('utf-8'), sha256).digest()) 

    rawtoken = { 
        'sr' :  uri, 
        'sig': signature, 
        'se' : str(int(ttl)), 
        'skn' : policy_name 
    } 

    return 'SharedAccessSignature ' + urlencode(rawtoken) 

print(generate_sas_token("myIdScope/registrations/mydeviceregistrationid", "00mysymmetrickey", "registration"))

结果应类似于以下输出:


SharedAccessSignature sr=myIdScope%2Fregistrations%2Fmydeviceregistrationid&sig=SDpdbUNk%2F1DSjEpeb29BLVe6gRDZI7T41Y4BPsHHoUg%3D&se=1630175722&skn=registration 

以下示例演示如何使用共享访问签名通过设备 API 进行身份验证。


curl -L -i -X PUT -H 'Content-Type: application/json' -H 'Content-Encoding:  utf-8' -H 'Authorization: [token]' -d '{"registrationId": "[registration_id]"}' https://global.azure-devices-provisioning.net/[ID_Scope]/registrations/[registration_id]/register?api-version=2021-06-01 

如果使用基于对称密钥的注册组,则需要首先使用注册组密钥生成 device symmetric 密钥。 使用注册组主密钥或辅助密钥来计算设备注册 ID 的 HMAC-SHA256。 然后,结果转换为 Base64 格式以获取派生的设备密钥。 若要查看代码示例,请参阅 派生设备密钥。 派生设备对称密钥后,可以使用前面的示例注册设备。

警告

若要避免在设备代码中包含组主密钥,应从设备中完成派生设备密钥的过程。

基于证书的身份验证

如果为基于 X.509 证书的身份验证设置单个注册或注册组,设备需要使用其颁发的 X.509 证书来证明设备 API。 请参阅以下文章,了解如何设置注册并生成设备证书。

设置注册并颁发设备证书后,以下示例演示如何使用设备的 X.509 证书向设备 API 进行身份验证。


curl -L -i -X PUT –cert ./[device_cert].pem –key ./[device_cert_private_key].pem -H 'Content-Type: application/json' -H 'Content-Encoding:  utf-8' -d '{"registrationId": "[registration_id]"}' https://global.azure-devices-provisioning.net/[ID_Scope]/registrations/[registration_id]/register?api-version=2021-06-01 

服务 API 身份验证

服务 API 用于检索注册状态并删除设备注册。 后端应用还使用此服务以编程方式管理 单个组注册组。 服务 API 支持后端应用的基于密钥的身份验证。

必须具有访问任何服务 API 终结点的适当权限。 例如,后端应用必须包含包含安全凭据的令牌,以及发送到服务的每个消息。

Azure IoT 中心设备预配服务通过针对共享访问策略验证令牌,授予对终结点的访问权限。 安全凭据(如对称密钥)永远不会通过网络发送。

访问控制和权限

可以通过以下方式授予 权限

  • 共享访问授权策略。 共享访问策略可以授予任意 权限组合。 可以在 Azure 门户中定义策略,也可以使用 设备预配服务 REST API 以编程方式定义策略。 新创建的预配服务具有以下默认策略:

  • provisioningserviceowner:具有所有权限的策略。 有关详细信息,请参阅 权限

注释

设备预配服务资源提供程序通过 Azure 订阅进行保护,Azure 资源管理器中的所有提供程序都受到保护。

有关如何构造和使用安全令牌的详细信息,请参阅下一部分。

HTTP 是唯一受支持的协议,它通过在 授权 请求标头中包含有效的令牌来实现身份验证。

Example

SharedAccessSignature sr = 
   mydps.azure-devices-provisioning.net&sig=kPszxZZZZZZZZZZZZZZZZZAhLT%2bV7o%3d&se=1487709501&skn=provisioningserviceowner`\

注释

连接到服务时 ,Azure IoT 中心设备预配服务 SDK 自动生成令牌。

安全令牌

设备预配服务使用安全令牌对服务进行身份验证,以避免在网络上发送密钥。 此外,安全令牌在时间有效性和范围方面受到限制。 Azure IoT 中心设备预配服务 SDK 自动生成令牌,而无需任何特殊配置。 某些方案要求你直接生成和使用安全令牌。 此类方案包括直接使用 HTTP 接口。

安全令牌结构

可使用安全令牌向服务授予限时访问 IoT 中心设备预配服务中特定功能的权限。 若要获取连接到预配服务的授权,服务必须发送使用共享访问或对称密钥签名的安全令牌。

使用共享访问密钥签名的令牌授予对与共享访问策略权限关联的所有功能的访问权限。

安全令牌具有以下格式:

SharedAccessSignature sig={signature}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}

下面是预期值

价值 Description
{signature} 表单的 HMAC-SHA256 签名字符串: {URL-encoded-resourceURI} + "\n" + expiry重要说明:密钥是从 base64 解码的,用作执行 HMAC-SHA256 计算的键。
{到期} 从纪元 1970 年 1 月 1日 00:00:00 UTC 时间至今秒数的 UTF8 字符串。
{URL-encoded-resourceURI} 小写资源 URI 的小写 URL 编码。 此令牌可访问的终结点的 URI 前缀(按分段),以 IoT 设备预配服务的主机名开头(无协议)。 例如,mydps.azure-devices-provisioning.net
{policyName} 此令牌引用的共享访问策略的名称。

注释

URI 前缀按段而不是字符计算。 例如,/a/b/a/b/c 的前缀,但不是 /a/bc 的前缀。

以下 Node.js 代码片段显示了一个名为 generateSasToken 的函数,该函数从输入 resourceUri, signingKey, policyName, expiresInMins中计算令牌。 后续部分详细介绍了如何初始化不同令牌用例的不同输入。

var generateSasToken = function(resourceUri, signingKey, policyName, expiresInMins) {
    resourceUri = encodeURIComponent(resourceUri);

    // Set expiration in seconds
    var expires = (Date.now() / 1000) + expiresInMins * 60;
    expires = Math.ceil(expires);
    var toSign = resourceUri + '\n' + expires;

    // Use crypto
    var hmac = crypto.createHmac('sha256', new Buffer(signingKey, 'base64'));
    hmac.update(toSign);
    var base64UriEncoded = encodeURIComponent(hmac.digest('base64'));

    // Construct authorization string
    var token = "SharedAccessSignature sr=" + resourceUri + "&sig="
    + base64UriEncoded + "&se=" + expires + "&skn="+ policyName;
    return token;
};

相比之下,生成安全令牌的等效 Python 代码为:

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, sha256).digest())

    rawtoken = {
        'sr' :  uri,
        'sig': signature,
        'se' : str(int(ttl)),
        'skn' : policy_name
    }

    return 'SharedAccessSignature ' + urlencode(rawtoken)

注释

由于令牌的时间有效性是在 IoT 设备预配服务计算机上进行验证的,因此生成令牌的计算机时钟偏移必须最小化。

使用服务组件中的安全令牌

服务组件只能使用共享访问策略生成安全令牌,并授予前面所述的相应权限。

下面是在端点上公开的服务函数:

端点 功能性
{your-service}.azure-devices-provisioning.net/enrollments 向设备预配服务提供设备注册操作。
{your-service}.azure-devices-provisioning.net/enrollmentGroups 提供用于管理设备注册组的操作。
{your-service}.azure-devices-provisioning.net/registrations/{id} 提供用于检索和管理设备注册状态的操作。

例如,使用预创建的名为enrollmentread的共享访问策略生成的服务将使用以下参数创建令牌:

  • 资源 URI: {mydps}.azure-devices-provisioning.net
  • 签名密钥:enrollmentread 策略的一部分密钥,
  • 策略名称: enrollmentread
  • 任何过期 time.backn
var endpoint ="mydps.azure-devices-provisioning.net";
var policyName = 'enrollmentread'; 
var policyKey = '...';

var token = generateSasToken(endpoint, policyKey, policyName, 60);

将会授予权限以读取所有注册记录,结果为:

SharedAccessSignature sr=mydps.azure-devices-provisioning.net&sig=JdyscqTpXdEJs49elIUCcohw2DlFDR3zfH5KqGJo4r4%3D&se=1456973447&skn=enrollmentread

SDK 和示例

参考文章:

以下参考文章提供了有关控制对 IoT 设备预配服务的访问权限的详细信息。

设备预配服务权限

下表列出了可用于控制对 IoT 设备预配服务的访问权限的权限。

许可 注释
ServiceConfig 授予更改服务配置的权限。
后端云服务使用此权限。
EnrollmentRead 授予对设备注册和注册组的读取访问权限。
后端云服务使用此权限。
EnrollmentWrite 授予对设备注册和注册组的写入访问权限。
后端云服务使用此权限。
RegistrationStatusRead 授予对设备注册状态的读取访问权限。
后端云服务使用此权限。
RegistrationStatusWrite 授予对设备注册状态的删除访问权限。
后端云服务使用此权限。