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

教程:使用注册组预配多个 X.509 设备

在本教程中,你将了解如何预配使用 X.509 证书进行身份验证的 IoT 设备组。 Azure IoT SDK 的示例设备代码将在开发计算机上执行,以模拟 x.509 设备的预配。 在实际设备上,将从 IoT 设备部署并运行设备代码。

Azure IoT 中心设备预配服务支持两种类型的预配设备注册:

  • 注册组:用于注册多个相关设备。 本教程演示如何使用注册组进行预配。
  • 单个注册:用于注册单个设备。

Azure IoT 中心设备预配服务支持用于预配设备的三种身份验证形式:

  • X.509 证书 - 此教程演示 X.509 证书证明
  • 受信任的平台模块 (TPM)
  • 对称密钥

在生产场景中,硬件安全模块 (HSM) 用于基于硬件安全地存储设备机密。 HSM 可以与对称密钥、X.509 证书或 TPM 证明配合使用,为机密提供安全存储。 建议使用基于硬件的设备机密存储来帮助保护敏感信息,例如设备证书的私钥。

在本教程中,你将完成以下目标:

  • 创建一个信任证书链以整理一组使用 X.509 证书的设备。
  • 创建使用证书链的新组注册。
  • 设置开发环境。
  • 使用 Azure IoT 设备 SDK 中的示例代码通过证书链预配设备。

先决条件

以下先决条件适用于用于模拟设备的 Windows 开发环境。 对于 Linux 或 macOS,请参阅 SDK 文档的准备开发环境中的相应部分。

  • 安装 Visual Studio 2022 并启用“使用 C++ 的桌面开发”工作负载。 Visual Studio 2015、Visual Studio 2017 和 Visual Studio 2019 也受支持。

  • 安装最新的 CMake 生成系统。 请确保选中将 CMake 可执行文件添加到路径的选项。

    重要

    在开始 CMake 安装之前,请确认已在计算机上安装 Visual Studio 必备组件(Visual Studio 和“使用 C++ 的桌面开发”工作负载)。 满足先决条件并验证下载内容后,安装 CMake 生成系统。 另请注意,旧版本的 CMake 生成系统无法生成本教程中使用的解决方案文件。 请确保使用最新版本的 CMake。

以下先决条件适用于 Windows 开发环境。 对于 Linux 或 macOS,请参阅 SDK 文档的准备开发环境中的相应部分。

  • 在基于 Windows 的计算机上安装 .NET SDK 6.0 或更高版本。 可使用以下命令来检查你的版本。

    dotnet --info
    

以下先决条件适用于 Windows 开发环境。 对于 Linux 或 macOS,请参阅 SDK 文档的准备开发环境中的相应部分。

以下先决条件适用于 Windows 开发环境。

以下先决条件适用于 Windows 开发环境。 对于 Linux 或 macOS,请参阅 SDK 文档的准备开发环境中的相应部分。

  • 安装最新版本的 Git。 确保将 Git 添加到可供命令窗口访问的环境变量。

  • 请确保已在计算机上安装 OpenSSL。 在 Windows 上, Git 安装包含 OpenSSL 安装。 可以从 Git Bash 提示符访问 OpenSSL。 若要验证是否安装了 OpenSSL,请打开 Git Bash 提示符并输入 openssl version

    注意

    除非你熟悉 OpenSSL 并且已将其安装在 Windows 计算机上,否则我们建议从 Git Bash 提示符使用 OpenSSL。 或者,可以选择下载源代码并生成 OpenSSL。 如果你确实选择生成或下载 OpenSSL,请确保可在你的路径中访问 OpenSSL 二进制文件,并且 OPENSSL_CNF 环境变量设置为 openssl.cnf 文件的路径。

准备开发环境

在本部分中,你需要准备一个用于生成 Azure IoT C SDK 的开发环境。 SDK 包括设备利用 DPS 进行预配时所用的示例代码和工具。

  1. 在 Web 浏览器中,转到 Azure IoT C SDK 的发布页面

  2. 复制最新版 Azure IoT C SDK 的标记名称,例如:lts_03_2024

  3. 打开 Windows 命令提示符,运行以下命令以克隆最新版本的适用于 C 的 Azure IoT 设备 SDK GitHub 存储库。 请将 <release-tag> 替换为在上一步骤中复制的标记。

    git clone -b <release-tag> https://github.com/Azure/azure-iot-sdk-c.git
    cd azure-iot-sdk-c
    git submodule update --init
    

    此操作可能需要几分钟才能完成。

  4. 操作完成后,从 azure-iot-sdk-c 目录运行以下命令:

    mkdir cmake
    cd cmake
    
  5. 代码示例使用 X.509 证书通过 X.509 身份验证提供证明。 运行以下命令,以生成特定于你的开发平台(包括设备预配客户端)的 SDK 版本。 在 cmake 目录中生成模拟设备的 Visual Studio 解决方案。

    <path 替换为克隆的 C SDK 的绝对路径。

    cmake -Duse_prov_client:BOOL=ON -Dhsm_custom_lib=c:/<path>/azure-iot-sdk-c/cmake/provisioning_client/samples/custom_hsm_example/Debug/custom_hsm_example.lib ..
    

    提示

    如果 cmake 找不到 C++ 编译器,则可能会在运行以上命令时出现生成错误。 如果出现这种情况,请尝试在 Visual Studio 命令提示符窗口中运行该命令。

  6. 生成成功后,最后的几个输出行如下所示:

    cmake -Duse_prov_client:BOOL=ON -Dhsm_custom_lib=c:/azure-iot-sdk-c/cmake/provisioning_client/samples/custom_hsm_example/Debug/custom_hsm_example.lib ..
    -- Building for: Visual Studio 17 2022
    -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.22000.
    -- The C compiler identification is MSVC 19.32.31329.0
    -- The CXX compiler identification is MSVC 19.32.31329.0
    
    ...
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: C:/azure-iot-sdk-c/cmake
    

打开 Windows 命令提示符,使用以下命令克隆适用于 C# 的 Azure IoT SDK GitHub 存储库:

git clone https://github.com/Azure/azure-iot-sdk-csharp.git

打开 Windows 命令提示符,使用以下命令克隆适用于 Node.js 的 Azure IoT SDK GitHub 存储库:

git clone https://github.com/Azure/azure-iot-sdk-node.git

打开 Windows 命令提示符,使用以下命令克隆适用于 Python 的 Azure IoT 设备 SDK GitHub 存储库:

git clone -b v2 https://github.com/Azure/azure-iot-sdk-python.git --recursive

注意

本教程中使用的示例位于 azure-iot-sdk-python 存储库的 v2 分支中。 Python SDK 的 V3 可用于 beta 版。

  1. 打开 Windows 命令提示符,使用以下命令克隆适用于 Java 的 Azure IoT 示例 GitHub 存储库:

    git clone https://github.com/Azure/azure-iot-sdk-java.git --recursive
    
  2. 转到 azure-iot-sdk-java 根目录,并生成项目以下载全部所需的包。

    cd azure-iot-sdk-java
    mvn install -DskipTests=true
    

创建 X.509 证书链

在本部分中,你将生成一个包含三个证书的 X.509 证书链,以便根据本教程的说明测试每台设备。 证书具有以下层次结构。

此图显示根 CA 证书、中间 CA 证书和设备证书之间的关系。

根证书:上传并使用 DPS 验证根证书。 此验证将使 DPS 能够信任该证书并验证由其签名的证书。

中间证书:中间证书通常用于按产品线、公司部门或其他标准对设备进行逻辑分组。 本教程使用包含一个中间证书的证书链,但在生产方案中,你可能有多个证书。 此链的中间证书由根证书签名。 此证书将提供给在 DPS 中创建的注册组。 此配置允许管理设备证书由同一中间证书签名的整组设备。

设备证书:设备证书(有时称为叶证书)由中间证书签名,并随其私钥一起存储在设备上。 理想情况下,这些敏感项将通过 HSM 安全地存储。 多个设备证书可由同一个中间证书签名。 尝试预配时,每台设备都会提供其证书、私钥以及证书链。

有关证书链的详细信息,请参阅 X.509 证书证明

设置 X.509 OpenSSL 环境

在本部分中,你将创建 Openssl 配置文件、目录结构和 Openssl 命令使用的其他文件。

  1. 打开 Git Bash 命令提示符,导航到要在其中生成用于本教程的 X.509 证书和密钥的文件夹。

  2. 为根 CA 证书创建名为 openssl_root_ca.cnf 的 OpenSSL 配置文件。 OpenSSL 配置文件包含 OpenSSL 命令使用的策略和定义。 将以下文本复制并粘贴到 openssl_root_ca.cnf 文件中:

    # OpenSSL root CA configuration file.
    
    [ ca ]
    default_ca = CA_default
    
    [ CA_default ]
    # Directory and file locations.
    dir               = .
    certs             = $dir/certs
    crl_dir           = $dir/crl
    new_certs_dir     = $dir/newcerts
    database          = $dir/index.txt
    serial            = $dir/serial
    RANDFILE          = $dir/private/.rand
    
    # The root key and root certificate.
    private_key       = $dir/private/azure-iot-test-only.root.ca.key.pem
    certificate       = $dir/certs/azure-iot-test-only.root.ca.cert.pem
    
    # For certificate revocation lists.
    crlnumber         = $dir/crlnumber
    crl               = $dir/crl/azure-iot-test-only.intermediate.crl.pem
    crl_extensions    = crl_ext
    default_crl_days  = 30
    
    # SHA-1 is deprecated, so use SHA-2 instead.
    default_md        = sha256
    
    name_opt          = ca_default
    cert_opt          = ca_default
    default_days      = 375
    preserve          = no
    policy            = policy_loose
    
    [ policy_strict ]
    # The root CA should only sign intermediate certificates that match.
    countryName             = optional
    stateOrProvinceName     = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied
    emailAddress            = optional
    
    [ policy_loose ]
    # Allow the intermediate CA to sign a more diverse range of certificates.
    countryName             = optional
    stateOrProvinceName     = optional
    localityName            = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied
    emailAddress            = optional
    
    [ req ]
    default_bits        = 2048
    distinguished_name  = req_distinguished_name
    string_mask         = utf8only
    
    # SHA-1 is deprecated, so use SHA-2 instead.
    default_md          = sha256
    
    # Extension to add when the -x509 option is used.
    x509_extensions     = v3_ca
    
    [ req_distinguished_name ]
    # See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
    countryName                     = Country Name (2 letter code)
    stateOrProvinceName             = State or Province Name
    localityName                    = Locality Name
    0.organizationName              = Organization Name
    organizationalUnitName          = Organizational Unit Name
    commonName                      = Common Name
    emailAddress                    = Email Address
    
    # Optionally, specify some defaults.
    countryName_default             = US
    stateOrProvinceName_default     = WA
    localityName_default            =
    0.organizationName_default      = My Organization
    organizationalUnitName_default  =
    emailAddress_default            =
    
    [ v3_ca ]
    # Extensions for a typical CA.
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:true
    keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    
    [ v3_intermediate_ca ]
    # Extensions for a typical intermediate CA.
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:true
    keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    
    [ usr_cert ]
    # Extensions for client certificates.
    basicConstraints = CA:FALSE
    nsComment = "OpenSSL Generated Client Certificate"
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    
    [ server_cert ]
    # Extensions for server certificates.
    basicConstraints = CA:FALSE
    nsComment = "OpenSSL Generated Server Certificate"
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer:always
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    
    [ crl_ext ]
    # Extension for CRLs.
    authorityKeyIdentifier=keyid:always
    
    [ ocsp ]
    # Extension for OCSP signing certificates.
    basicConstraints = CA:FALSE
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    keyUsage = critical, digitalSignature
    extendedKeyUsage = critical, OCSPSigning
    
  3. 创建名为 openssl_device_intermediate_ca.cnf 的 OpenSSL 配置文件,以用于中间证书和设备证书。 将以下文本复制并粘贴到 openssl_device_intermediate_ca.cnf 文件中:

    # OpenSSL root CA configuration file.
    
    [ ca ]
    default_ca = CA_default
    
    [ CA_default ]
    # Directory and file locations.
    dir               = .
    certs             = $dir/certs
    crl_dir           = $dir/crl
    new_certs_dir     = $dir/newcerts
    database          = $dir/index.txt
    serial            = $dir/serial
    RANDFILE          = $dir/private/.rand
    
    # The root key and root certificate.
    private_key       = $dir/private/azure-iot-test-only.intermediate.key.pem
    certificate       = $dir/certs/azure-iot-test-only.intermediate.cert.pem
    
    # For certificate revocation lists.
    crlnumber         = $dir/crlnumber
    crl               = $dir/crl/azure-iot-test-only.intermediate.crl.pem
    crl_extensions    = crl_ext
    default_crl_days  = 30
    
    # SHA-1 is deprecated, so use SHA-2 instead.
    default_md        = sha256
    
    name_opt          = ca_default
    cert_opt          = ca_default
    default_days      = 375
    preserve          = no
    policy            = policy_loose
    
    [ policy_strict ]
    # The root CA should only sign intermediate certificates that match.
    countryName             = optional
    stateOrProvinceName     = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied
    emailAddress            = optional
    
    [ policy_loose ]
    # Allow the intermediate CA to sign a more diverse range of certificates.
    countryName             = optional
    stateOrProvinceName     = optional
    localityName            = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied
    emailAddress            = optional
    
    [ req ]
    default_bits        = 2048
    distinguished_name  = req_distinguished_name
    string_mask         = utf8only
    
    # SHA-1 is deprecated, so use SHA-2 instead.
    default_md          = sha256
    
    # Extension to add when the -x509 option is used.
    x509_extensions     = v3_ca
    
    [ req_distinguished_name ]
    # See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
    countryName                     = Country Name (2 letter code)
    stateOrProvinceName             = State or Province Name
    localityName                    = Locality Name
    0.organizationName              = Organization Name
    organizationalUnitName          = Organizational Unit Name
    commonName                      = Common Name
    emailAddress                    = Email Address
    
    # Optionally, specify some defaults.
    countryName_default             = US
    stateOrProvinceName_default     = WA
    localityName_default            =
    0.organizationName_default      = My Organization
    organizationalUnitName_default  =
    emailAddress_default            =
    
    [ v3_ca ]
    # Extensions for a typical CA.
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:true
    keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    
    [ v3_intermediate_ca ]
    # Extensions for a typical intermediate CA.
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:true
    keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    
    [ usr_cert ]
    # Extensions for client certificates.
    basicConstraints = CA:FALSE
    nsComment = "OpenSSL Generated Client Certificate"
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    
    [ server_cert ]
    # Extensions for server certificates.
    basicConstraints = CA:FALSE
    nsComment = "OpenSSL Generated Server Certificate"
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer:always
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    
    [ crl_ext ]
    # Extension for CRLs.
    authorityKeyIdentifier=keyid:always
    
    [ ocsp ]
    # Extension for OCSP signing certificates.
    basicConstraints = CA:FALSE
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    keyUsage = critical, digitalSignature
    extendedKeyUsage = critical, OCSPSigning
    
  4. 创建目录结构、数据库文件 index.txt,以及本教程中的 OpenSSL 命令使用的序列号文件 serial

    mkdir certs csr newcerts private
    touch index.txt
    openssl rand -hex 16 > serial
    

创建根 CA 证书

运行以下命令以创建根 CA 私钥和根 CA 证书。 你将使用此证书和密钥对中间证书进行签名。

  1. 在 Git Bash 终端中,创建根 CA 私钥:

    openssl genrsa -aes256 -passout pass:1234 -out ./private/azure-iot-test-only.root.ca.key.pem 4096
    
  2. 创建根 CA 证书:

    openssl req -new -x509 -config ./openssl_root_ca.cnf -passin pass:1234 -key ./private/azure-iot-test-only.root.ca.key.pem -subj '//CN=Azure IoT Hub CA Cert Test Only' -days 30 -sha256 -extensions v3_ca -out ./certs/azure-iot-test-only.root.ca.cert.pem
    

    重要

    仅当需要在 Windows 平台上使用 Git 来转义字符串时,才需要为使用者名称 (//CN=Azure IoT Hub CA Cert Test Only) 提供额外的西文斜杠。 在 Linux 平台上,请提供包含仅一个正斜杠的使用者名称 (/CN=Azure IoT Hub CA Cert Test Only)。

  3. 检查根 CA 证书:

    openssl x509 -noout -text -in ./certs/azure-iot-test-only.root.ca.cert.pem
    

    请注意,颁发者和使用者都是根 CA。

    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                1d:93:13:0e:54:07:95:1d:8c:57:4f:12:14:b9:5e:5f:15:c3:a9:d4
            Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN = Azure IoT Hub CA Cert Test Only
            Validity
                Not Before: Jun 20 22:52:23 2022 GMT
                Not After : Jul 20 22:52:23 2022 GMT
            Subject: CN = Azure IoT Hub CA Cert Test Only
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                    RSA Public-Key: (4096 bit)
    

创建中间 CA 证书

请运行以下命令以创建中间 CA 私钥和中间 CA 证书。 你将使用此证书和密钥对设备证书进行签名。

  1. 在 Git Bash 终端中,创建中间 CA 私钥:

    openssl genrsa -aes256 -passout pass:1234 -out ./private/azure-iot-test-only.intermediate.key.pem 4096
    
  2. 创建中间 CA 证书签名请求 (CSR):

    openssl req -new -sha256 -passin pass:1234 -config ./openssl_device_intermediate_ca.cnf -subj '//CN=Azure IoT Hub Intermediate Cert Test Only' -key ./private/azure-iot-test-only.intermediate.key.pem -out ./csr/azure-iot-test-only.intermediate.csr.pem
    

    重要

    仅当需要在 Windows 平台上使用 Git 来转义字符串时,才需要为使用者名称 (//CN=Azure IoT Hub Intermediate Cert Test Only) 提供额外的西文斜杠。 在 Linux 平台上,请提供包含单个正斜杠的使用者名称 (/CN=Azure IoT Hub Intermediate Cert Test Only)。

  3. 使用根 CA 证书对中间证书进行签名

    openssl ca -batch -config ./openssl_root_ca.cnf -passin pass:1234 -extensions v3_intermediate_ca -days 30 -notext -md sha256 -in ./csr/azure-iot-test-only.intermediate.csr.pem -out ./certs/azure-iot-test-only.intermediate.cert.pem
    
  4. 检查中间 CA 证书:

    openssl x509 -noout -text -in ./certs/azure-iot-test-only.intermediate.cert.pem
    

    请注意,颁发者是根 CA,使用者是中间 CA。

    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                d9:55:87:57:41:c8:4c:47:6c:ee:ba:83:5d:ae:db:39
            Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN = Azure IoT Hub CA Cert Test Only
            Validity
                Not Before: Jun 20 22:54:01 2022 GMT
                Not After : Jul 20 22:54:01 2022 GMT
            Subject: CN = Azure IoT Hub Intermediate Cert Test Only
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                    RSA Public-Key: (4096 bit)
    

创建设备证书

在本部分中,你将创建两个设备证书及其完整链证书。 完整链证书包含设备证书、中间 CA 证书和根 CA 证书。 设备在向 DPS 注册时必须提供其完整链证书。

  1. 创建第一个设备私钥。

    openssl genrsa -out ./private/device-01.key.pem 4096
    
  2. 创建设备证书 CSR。

    必须将设备证书的使用者公用名 (CN) 设置为设备在向 DPS 注册时使用的注册 ID。 注册 ID 是一个不区分大小写的字符串,包含字母数字字符和以下特殊字符:'-''.''_'':'。 最后一个字符必须是字母数字或短划线 ('-')。 公用名必须遵循此格式。 DPS 支持最大长度为 128 个字符的注册 ID;但是,在 X.509 证书中,使用者公用名的最大长度为 64 个字符。 因此,使用 X.509 证书时,注册 ID 的长度限制为 64 个字符。 对于组注册,注册 ID 也用作 IoT 中心的设备 ID。

    使用者公用名是使用参数 -subj 设置的。 在以下命令中,公用名设置为 device-01。

    openssl req -config ./openssl_device_intermediate_ca.cnf -key ./private/device-01.key.pem -subj '//CN=device-01' -new -sha256 -out ./csr/device-01.csr.pem
    

    重要

    仅当需要在 Windows 平台上使用 Git 来转义字符串时,才需要为使用者名称 (//CN=device-01) 提供额外的西文斜杠。 在 Linux 平台上,请提供包含单个正斜杠的使用者名称 (/CN=device-01)。

  3. 对设备证书进行签名。

    openssl ca -batch -config ./openssl_device_intermediate_ca.cnf -passin pass:1234 -extensions usr_cert -days 30 -notext -md sha256 -in ./csr/device-01.csr.pem -out ./certs/device-01.cert.pem
    
  4. 检查设备证书:

    openssl x509 -noout -text -in ./certs/device-01.cert.pem
    

    请注意,颁发者是中间 CA,使用者是设备注册 ID device-01

    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                d9:55:87:57:41:c8:4c:47:6c:ee:ba:83:5d:ae:db:3a
            Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN = Azure IoT Hub Intermediate Cert Test Only
            Validity
                Not Before: Jun 20 22:55:39 2022 GMT
                Not After : Jul 20 22:55:39 2022 GMT
            Subject: CN = device-01
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                    RSA Public-Key: (4096 bit)
    
  5. 设备在向 DPS 进行身份验证时必须提供完整的证书链。 使用以下命令以创建证书链:

    cat ./certs/device-01.cert.pem ./certs/azure-iot-test-only.intermediate.cert.pem ./certs/azure-iot-test-only.root.ca.cert.pem > ./certs/device-01-full-chain.cert.pem
    
  6. 在文本编辑器中打开证书链文件 ./certs/device-01-full-chain.cert.pem 以对其进行检查。 证书链文本包含所有三个证书的完整链。 在本教程的稍后部分,你将使用此证书链来预配 device-01

    完整链文本采用以下格式:

    -----BEGIN CERTIFICATE-----
        <Text for the device certificate includes public key>
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
        <Text for the intermediate certificate includes public key>
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
        <Text for the root certificate includes public key>
    -----END CERTIFICATE-----
    
  7. 若要为第二台设备创建私钥、X.509 证书和完整链证书,请将此脚本复制并粘贴到 Git Bash 命令提示符下。 若要为更多设备创建证书,可以修改脚本开头声明的 registration_id 变量。

    registration_id=device-02
    echo $registration_id
    openssl genrsa -out ./private/${registration_id}.key.pem 4096
    openssl req -config ./openssl_device_intermediate_ca.cnf -key ./private/${registration_id}.key.pem -subj "//CN=$registration_id" -new -sha256 -out ./csr/${registration_id}.csr.pem
    openssl ca -batch -config ./openssl_device_intermediate_ca.cnf -passin pass:1234 -extensions usr_cert -days 30 -notext -md sha256 -in ./csr/${registration_id}.csr.pem -out ./certs/${registration_id}.cert.pem
    cat ./certs/${registration_id}.cert.pem ./certs/azure-iot-test-only.intermediate.cert.pem ./certs/azure-iot-test-only.root.ca.cert.pem > ./certs/${registration_id}-full-chain.cert.pem
    

    重要

    仅当需要在 Windows 平台上使用 Git 来转义字符串时,才需要为使用者名称 (//CN=$registration_id) 提供额外的西文斜杠。 在 Linux 平台上,请提供包含单个正斜杠的使用者名称 (/CN=$registration_id)。

    注意

    此脚本使用注册 ID 作为私钥和证书文件的基文件名。 如果注册 ID 包含无效的文件名字符,则需要相应地修改脚本。

    警告

    证书的文本仅包含公钥信息。

    但是,设备还必须能够访问设备证书的私钥。 这是必需的,因为设备在尝试预配时必须在运行时使用该密钥执行验证。 此密钥的敏感性是建议在实际 HSM 中使用基于硬件的存储来帮助保护私钥的主要原因之一。

在本教程的其余部分,你将使用以下文件:

证书 文件 说明
根 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-full-chain.cert.pem 由设备提供,用于向 DPS 进行身份验证和注册。
device-02 私钥 private/device-02.key.pem 由设备用于在向 DPS 进行身份验证期间验证设备证书的所有权。
device-02 完整链证书 certs/device-02-full-chain.cert.pem 由设备提供,用于向 DPS 进行身份验证和注册。

验证根证书的所有权

要使 DPS 能够在身份验证期间验证设备的证书链,你必须上传并验证根 CA 证书的所有权。 若要将根 CA 证书添加到 DPS 实例,请执行以下步骤:

  1. Azure 门户中导航到设备预配服务实例。

  2. 从左侧菜单中打开“证书”,然后选择“添加”以添加新证书。

  3. 输入证书的友好显示名称。 浏览到根 CA 证书文件 certs/azure-iot-test-only.root.ca.cert.pem 所在的位置。 选择“上传”。

  4. 选中“在上传时将证书状态设置为已验证”旁边的复选框

    展示了添加根 CA 证书和选中“在上传时将证书状态设置为已验证”复选框的屏幕截图。

  5. 选择“保存”。

  6. 请确保证书显示在证书选项卡中,状态为“已验证”。

    显示证书列表中已验证的根 CA 证书的屏幕截图。

在基于 Windows 的设备上更新证书存储

在非 Windows 设备上,可以将代码中的证书链作为证书存储传递。

在基于 Windows 的设备上,必须将签名证书(根证书和中间证书)添加到 Windows 证书存储。 否则,签名证书将不会通过具有传输层安全性 (TLS) 的安全通道传输到 DPS。

提示

还可以将 OpenSSL 与 C SDK 一起使用,而不是使用安全通道 (Schannel)。 有关使用 OpenSSL 的详细信息,请参阅在 SDK 中使用 OpenSSL

若要将签名证书添加到基于 Windows 的设备中的证书存储,请执行以下操作:

  1. 在 Git Bash 终端中,将签名证书转换为 .pfx,如下所示。

    根 CA 证书:

    openssl pkcs12 -inkey ./private/azure-iot-test-only.root.ca.key.pem -in ./certs/azure-iot-test-only.root.ca.cert.pem -export -passin pass:1234 -passout pass:1234 -out ./certs/root.pfx
    

    中间 CA 证书:

    openssl pkcs12 -inkey ./private/azure-iot-test-only.intermediate.key.pem -in ./certs/azure-iot-test-only.intermediate.cert.pem -export -passin pass:1234 -passout pass:1234 -out ./certs/intermediate.pfx
    
  2. 右键单击 Windows“开始”按钮,然后选择“运行”。 输入“certmgr.msc”,然后选择“确定”以启动证书管理器

  3. 在证书管理器的“证书 - 当前用户”下,选择“受信任的根证书颁发机构”。 然后在菜单上选择“操作”>“所有任务”>“导入”

  4. 按照证书导入向导中的步骤导入 root.pfx

    • 请确保按“个人信息交换 (.pfx)”进行搜索
    • 使用 1234 作为密码。
    • 将证书放在“受信任的根证书颁发机构”证书存储中。
  5. 重复这些证书管理器步骤以导入 intermediate.pfx

    • 将证书放入“中间证书颁发机构”证书存储中。

你的签名证书现在在基于 Windows 的设备上受信任,且整个链可以传输到 DPS。

创建注册组

  1. 登录到 Azure 门户,并导航到设备预配服务实例。

  2. 从导航菜单的“设置”部分选择“管理注册”。

  3. 在页面顶部,选择“添加注册组”。

  4. 在“添加注册组”页的“注册 + 预配”选项卡上,提供以下信息以配置注册组详细信息:

    字段 说明
    证明 如果要上传仅用于此注册组的中间证书,请选择“X.509 中间证书”作为 证明机制;如果已上传中间证书,请选择“上传到此设备预配服务的 X.509 证书”。
    X.509 证书设置 根据所选的证明方法,为此注册组上传或选择主要和辅助中间证书。
    组名 提供设备组的名称。 注册组名称是一个不区分大小写的字符串(最大长度为 128 个字符),包含字母数字字符和以下特殊字符:'-''.''_'':'。 最后一个字符必须是字母数字或短划线 ('-')。
    预配状态 如果希望此注册组用于预配设备,请选中“启用此注册”复选框。 如果希望禁用该组,请取消选中此框。 稍后可以更改此设置。
    重新预配策略 选择一个重新预配策略,反映希望 DPS 如何处理请求重新预配的设备。 有关详细信息,请参阅重新预配策略

    屏幕截图显示如何为 X.509 证书证明添加注册组。

  5. 选择“下一步:IoT 中心”。

  6. 在“添加注册组”页的“IoT 中心”选项卡上,提供以下信息以确定注册组可以预配设备的目标 IoT 中心:

    字段 说明
    目标 IoT 中心 选择一个或多个链接的 IoT 中心,或向 IoT 中心添加新链接。 若要详细了解如何将 IoT 中心链接到 DPS 实例,请参阅如何链接和管理 IoT 中心
    分配策略 如果选择了多个已链接的 IoT 中心,请选择要将设备分配到不同中心的方式。 如需详细了解分配策略,请参阅如何使用分配策略

    如果仅选择了一个已链接的 IoT 中心,建议使用均匀加权分发策略。

    屏幕截图显示如何将 IoT 中心连接到新注册组。

  7. 选择“下一步:设备设置

  8. 在“添加注册组”页的“设备设置”选项卡上,提供以下信息以定义新预配设备的配置方式:

    字段 说明
    IoT Edge 如果通过此组预配的所有设备都将运行 Azure IoT Edge,请选中“在预配的设备上启用 IoT Edge”。 如果此组仅适用于未启用 IoT Edge 的设备,请取消选中此框。 同组的所有设备都将启用 IoT Edge,或者全都不启用。
    设备标记 使用此文本框可提供要应用于预配设备的设备孪生的任何标记。
    所需属性 使用此文本框可提供要应用于预配设备的设备孪生的任何所需属性。

    有关详细信息,请参阅了解并在 IoT 中心内使用设备孪生

  9. 在完成时选择“下一步:查看 + 创建”。

  10. 在“审阅 + 创建”选项卡上,验证你的全部值,然后选择“创建”。

准备和运行设备预配代码

在本部分中,通过设备预配服务实例信息更新示例代码。 如果设备经过身份验证,将会被分配到与本部分中配置的设备预配服务实例链接的 IoT 中心。

在本部分,你将使用 Git Bash 提示符和 Visual Studio IDE。

配置预配设备代码

在本部分中,通过设备预配服务实例信息更新示例代码。

  1. 在 Azure 门户中,选择设备预配服务实例的“概述”选项卡,记下“ID 范围”的值。

    显示 DPS 概述窗格中的 ID 范围的屏幕截图。

  2. 启动 Visual Studio 并打开在 azure-iot-sdk-c git 存储库根目录中创建的 cmake 目录中创建的新解决方案文件。 解决方案文件命名为 azure_iot_sdks.sln

  3. 在 Visual Studio 的解决方案资源管理器中,导航到“Provision_Samples”>“prov_dev_client_sample”>“源文件”,然后打开“prov_dev_client_sample.c”。

  4. 找到 id_scope 常量,将值替换为前面复制的“ID 范围”值。 例如:

    static const char* id_scope = "0ne00000A0A";
    
  5. 在同一文件中找到 main() 函数的定义。 确保将 hsm_type 变量设置为 SECURE_DEVICE_TYPE_X509,并注释掉所有其他 hsm_type 行。例如:

    SECURE_DEVICE_TYPE hsm_type;
    //hsm_type = SECURE_DEVICE_TYPE_TPM;
    hsm_type = SECURE_DEVICE_TYPE_X509;
    //hsm_type = SECURE_DEVICE_TYPE_SYMMETRIC_KEY;
    
  6. 保存所做更改。

  7. 右键单击“prov_dev_client_sample”项目,然后选择“设为启动项目”。

配置自定义 HSM 存根代码

与实际的基于硬件的安全存储交互的细节因设备硬件而异。 本教程中模拟设备使用的证书链将硬编码到自定义 HSM 存根代码中。 在实际情况中,证书链将存储在实际的 HSM 硬件中,以便为敏感信息提供更好的安全性。 然后将实现与此示例中使用的存根方法类似的方法,以从基于硬件的存储中读取机密。

尽管并未要求使用 HSM 硬件,但建议保护敏感信息(如证书的私钥)。 如果该示例调用了实际的 HSM,则私钥不会出现在源代码中。 将密钥包含在源代码中会将密钥公开给可以查看代码的任何人。 本教程中这样做只是为了帮助学习。

若要更新自定义 HSM 存根代码以模拟 ID 为 device-01 的设备的标识,请执行以下操作:

  1. 在 Visual Studio 的解决方案资源管理器中,导航到“Provision_Samples”>“custom_hsm_example”>“源文件”,然后打开“custom_hsm_example.c”。

  2. 使用生成设备证书时使用的公用名称更新 COMMON_NAME 字符串常量的字符串值。

    static const char* const COMMON_NAME = "device-01";
    
  3. 在 Git Bash 终端中运行以下命令,为设备证书生成字符串常量:

    sed -e 's/^/"/;$ !s/$/""\\n"/;$ s/$/"/' ./certs/device-01-full-chain.cert.pem
    

    请复制此命令的输出。

  4. 使用在生成证书后保存在 ./certs/device-01-full-chain.cert.pem 中的证书链来更新 CERTIFICATE 常量字符串的字符串值。 请将上一步的输出证书文本用于常量值。

    证书文本的语法必须遵循下面的模式,并且不使用额外的空格或由 Visual Studio 进行分析。

    // <Device/leaf cert>
    // <intermediates>
    // <root>
    static const char* const CERTIFICATE = "-----BEGIN CERTIFICATE-----\n"
    "MIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n"
        ...
    "MDMwWhcNMjAxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n"
    "-----END CERTIFICATE-----\n"
    "-----BEGIN CERTIFICATE-----\n"
    "MIIFPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQDDB9BenVy\n"
        ...
    "MTEyMjIxMzAzM1owNDEyMDAGA1UEAwwpQXp1cmUgSW9UIEh1YiBJbnRlcm1lZGlh\n"
    "-----END CERTIFICATE-----\n"
    "-----BEGIN CERTIFICATE-----\n"
    "MIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n"
        ...
    "MDMwWhcNMjAxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n"
    "-----END CERTIFICATE-----";        
    
  5. 在 Git Bash 中运行以下命令,为设备私钥生成字符串常量:

    sed -e 's/^/"/;$ !s/$/""\\n"/;$ s/$/"/' ./private/device-01.key.pem
    

    请复制此命令的输出。

  6. 使用设备证书的私钥更新 PRIVATE_KEY 常量的字符串值。 请将上一步的输出私钥文本用于常量值。

    私钥文本的语法必须遵循下面的模式,并且不使用额外的空格或由 Visual Studio 进行分析。

    static const char* const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n"
    "MIIJJwIBAAKCAgEAtjvKQjIhp0EE1PoADL1rfF/W6v4vlAzOSifKSQsaPeebqg8U\n"
        ...
    "X7fi9OZ26QpnkS5QjjPTYI/wwn0J9YAwNfKSlNeXTJDfJ+KpjXBcvaLxeBQbQhij\n"
    "-----END RSA PRIVATE KEY-----";
    
  7. 保存所做更改。

  8. 右键单击“custom_hsm_example”项目并选择“生成”。

    重要

    在下一部分生成解决方案的其余内容之前,必须生成 custom_hsm_example 项目。

运行示例

  1. 在 Visual Studio 菜单中,选择“调试”>“开始执行(不调试)”以运行该解决方案。 系统提示重新生成项目时,请选择“是”,以便在运行项目之前重新生成项目。

    以下输出是模拟设备 device-01 成功启动并连接到预配服务的示例。 设备已分配到 IoT 中心并注册:

    Provisioning API Version: 1.8.0
    
    Registering Device
    
    Provisioning Status: PROV_DEVICE_REG_STATUS_CONNECTED
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    
    Registration Information received from service: contoso-hub-2.azure-devices.net, deviceId: device-01
    Press enter key to exit:
    
  2. 对第二台设备 (device-02) 重复配置自定义 HSM 存根代码中的步骤,并再次运行示例。 为该设备使用以下值:

    说明
    公用名 "device-02"
    完整的证书链 使用 ./certs/device-02-full-chain.cert.pem 生成文本
    私钥 使用 ./private/device-02.key.pem 生成文本

    以下输出是模拟设备 device-02 成功启动并连接到预配服务的示例。 设备已分配到 IoT 中心并注册:

    Provisioning API Version: 1.8.0
    
    Registering Device
    
    Provisioning Status: PROV_DEVICE_REG_STATUS_CONNECTED
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    
    Registration Information received from service: contoso-hub-2.azure-devices.net, deviceId: device-02
    Press enter key to exit:
    

C# 示例代码设置为使用在受密码保护的 PKCS#12 格式文件 (.pfx) 中存储的 X.509 证书。 先前创建的完整链证书采用 PEM 格式。 若要将完整链证书转换为 PKCS#12 格式,请在 Git Bash 提示符下,从先前运行 OpenSSL 命令的目录输入以下命令。

  • device-01

    openssl pkcs12 -inkey ./private/device-01.key.pem -in ./certs/device-01-full-chain.cert.pem -export -passin pass:1234 -passout pass:1234 -out ./certs/device-01-full-chain.cert.pfx
    
  • device-02

    openssl pkcs12 -inkey ./private/device-02.key.pem -in ./certs/device-02-full-chain.cert.pem -export -passin pass:1234 -passout pass:1234 -out ./certs/device-02-full-chain.cert.pfx
    

在本部分的其余部分中,请使用 Windows 命令提示符。

  1. 在 Azure 门户中,选择设备预配服务的“概述”选项卡。

  2. 复制“ID 范围”值。

    Azure 门户上“ID 范围”的屏幕截图。

  3. 在 Windows 命令提示符下切换到 X509Sample 目录。 此目录位于前面步骤中克隆的 SDK 存储库中:.\azure-iot-sdk-csharp\provisioning\device\samples\getting started\X509Sample

  4. 输入以下命令,以生成并运行 X.509 设备预配示例。 将 <id-scope> 替换为你从 Azure 门户复制的 ID 范围。 将 <your-certificate-folder> 替换为在其中运行了 OpenSSL 命令的文件夹的路径。

    dotnet run -- -s <id-scope> -c <your-certificate-folder>\certs\device-01-full-chain.cert.pfx -p 1234
    

    设备将连接到 DPS 并分配到 IoT 中心。 随后设备将向 IoT 中心发送遥测消息。 应会看到与如下示例类似的输出:

    Loading the certificate...
    Found certificate: 3E5AA3C234B2032251F0135E810D75D38D2AA477 CN=Azure IoT Hub CA Cert Test Only; PrivateKey: False
    Found certificate: 81FE182C08D18941CDEEB33F53F8553BA2081E60 CN=Azure IoT Hub Intermediate Cert Test Only; PrivateKey: False
    Found certificate: 5BA1DB226D50EBB7A6A6071CED4143892855AE43 CN=device-01; PrivateKey: True
    Using certificate 5BA1DB226D50EBB7A6A6071CED4143892855AE43 CN=device-01
    Initializing the device provisioning client...
    Initialized for registration Id device-01.
    Registering with the device provisioning service...
    Registration status: Assigned.
    Device device-01 registered to contoso-hub-2.azure-devices.net.
    Creating X509 authentication for IoT Hub...
    Testing the provisioned device with IoT Hub...
    Sending a telemetry message...
    Finished.
    

    注意

    如果你未在命令行中指定证书和密码,则证书文件将默认为 ./certificate.pfx,系统将提示你输入密码。

    可以传递其他参数来更改 TransportType (-t) 和 GlobalDeviceEndpoint (-g)。 若要获取参数的完整列表,请键入 dotnet run -- --help

  5. 若要注册第二台设备,请使用其完整链证书重新运行示例。

    dotnet run -- -s <id-scope> -c <your-certificate-folder>\certs\device-02-full-chain.cert.pfx -p 1234
    

在以下步骤中,请使用 Windows 命令提示符。

  1. 在 Azure 门户中,选择设备预配服务的“概述”选项卡。

  2. 复制“ID 范围”值。

    Azure 门户中的 ID 范围的屏幕截图。

  3. 在 Windows 命令提示符下,转到示例目录,并安装示例所需的包。 显示的路径相对于 SDK 克隆到的位置。

    cd .\azure-iot-sdk-node\provisioning\device\samples
    npm install
    
  4. 在 provisioning\device\samples 文件夹中,打开 register_x509.js 并查看代码。

    示例默认使用 MQTT 作为传输协议。 如果要使用不同的协议,请注释掉以下行并取消注释相应协议的行。

    var ProvisioningTransport = require('azure-iot-provisioning-device-mqtt').Mqtt;
    

    此示例使用五个环境变量,通过 DPS 对 IoT 设备进行身份验证和预配。 这些环境变量包括:

    变量名称 说明
    PROVISIONING_HOST 用于连接到 DPS 实例的终结点。 对于本教程,请使用全局终结点 global.azure-devices-provisioning.net
    PROVISIONING_IDSCOPE DPS 实例的 ID 范围。
    PROVISIONING_REGISTRATION_ID 设备的注册 ID。 它必须与设备证书中的使用者公用名匹配。
    CERTIFICATE_FILE 设备完整链证书文件的路径。
    KEY_FILE 设备证书私钥文件的路径。

    ProvisioningDeviceClient.register() 方法尝试注册设备。

  5. 为全局设备终结点和 ID 范围添加环境变量。 将 <id-scope> 替换为你从 Azure 门户复制的值。

    set PROVISIONING_HOST=global.azure-devices-provisioning.net
    set PROVISIONING_IDSCOPE=<id-scope>
    
  6. 设置设备注册 ID 的环境变量。 IoT 设备的注册 ID 必须与设备证书上的使用者公用名匹配。 对于本教程,device-01 既是使用者名称,又是设备的注册 ID。

    set PROVISIONING_REGISTRATION_ID=device-01
    
  7. 为先前生成的设备完整链证书和设备私钥文件设置环境变量。 将 <your-certificate-folder> 替换为在其中运行了 OpenSSL 命令的文件夹的路径。

    set CERTIFICATE_FILE=<your-certificate-folder>\certs\device-01-full-chain.cert.pem
    set KEY_FILE=<your-certificate-folder>\private\device-01.key.pem
    
  8. 运行示例,然后验证设备是否已成功预配。

    node register_x509.js
    

    应会看到与如下示例类似的输出:

    registration succeeded
    assigned hub=contoso-hub-2.azure-devices.net
    deviceId=device-01
    Client connected
    send status: MessageEnqueued
    
  9. 根据下表更新第二台设备 (device-02) 的环境变量,然后再次运行该示例。

    环境变量
    PROVISIONING_REGISTRATION_ID device-02
    CERTIFICATE_FILE <your-certificate-folder>\certs\device-02-full-chain.cert.pem
    KEY_FILE <your-certificate-folder>\private\device-02.key.pem

在以下步骤中,请使用 Windows 命令提示符。

  1. 在 Azure 门户中,选择设备预配服务的“概述”选项卡。

  2. 复制“ID 范围”。

    Azure 门户中的 ID 范围的屏幕截图。

  3. 在 Windows 命令提示符下,转到 provision_x509.py 示例的目录。 显示的路径相对于 SDK 克隆到的位置。

    cd .\azure-iot-sdk-python\samples\async-hub-scenarios
    

    此示例使用六个环境变量,通过 DPS 对 IoT 设备进行身份验证和预配。 这些环境变量包括:

    变量名称 说明
    PROVISIONING_HOST 用于连接到 DPS 实例的终结点。 对于本教程,请使用全局终结点 global.azure-devices-provisioning.net
    PROVISIONING_IDSCOPE DPS 实例的 ID 范围。
    DPS_X509_REGISTRATION_ID 设备的注册 ID。 它必须与设备证书中的使用者公用名匹配。
    X509_CERT_FILE 设备完整链证书文件的路径。
    X509_KEY_FILE 设备证书私钥文件的路径。
    PASS_PHRASE 用于加密私钥文件(如果使用)的通行短语。 在本教程中不需要。
  4. 为全局设备终结点和 ID 范围添加这些环境变量。 对于 ID 范围变量,请使用从 Azure 门户复制的值。

    set PROVISIONING_HOST=global.azure-devices-provisioning.net
    set PROVISIONING_IDSCOPE=<ID scope for your DPS resource>
    
  5. 设置设备注册 ID 的环境变量。 IoT 设备的注册 ID 必须与设备证书上的使用者公用名匹配。 对于本教程,device-01 既是使用者名称,又是设备的注册 ID。

    set DPS_X509_REGISTRATION_ID=device-01
    
  6. 为先前生成的设备完整链证书和设备私钥文件设置环境变量。 将 <your-certificate-folder> 替换为在其中运行了 OpenSSL 命令的文件夹的路径。

    set X509_CERT_FILE=<your-certificate-folder>\certs\device-01-full-chain.cert.pem
    set X509_KEY_FILE=<your-certificate-folder>\private\device-01.key.pem
    
  7. 查看 provision_x509.py 的代码。 如果未使用 Python 版本 3.7 或更高版本,请执行此处所述的代码更改以替换 asyncio.run(main())

  8. 运行该示例。 该示例将连接到 DPS,后者会将设备预配到 IoT 中心。 预配设备后,示例将向 IoT 中心发送一些测试消息。

    python provision_x509.py
    

    应会看到与如下示例类似的输出:

    The complete registration result is
    device-01
    contoso-hub-2.azure-devices.net
    initialAssignment
    null
    Will send telemetry from the provisioned device
    sending message #1
    sending message #2
    sending message #3
    sending message #4
    sending message #5
    sending message #6
    sending message #7
    sending message #8
    sending message #9
    sending message #10
    done sending message #1
    done sending message #2
    done sending message #3
    done sending message #4
    done sending message #5
    done sending message #6
    done sending message #7
    done sending message #8
    done sending message #9
    done sending message #10
    
  9. 根据下表更新第二台设备 (device-02) 的环境变量,然后再次运行该示例。

    环境变量
    DPS_X509_REGISTRATION_ID device-02
    X509_CERT_FILE <your-certificate-folder>\certs\device-02-full-chain.cert.pem
    X509_KEY_FILE <your-certificate-folder>\private\device-02.key.pem

在以下步骤中,你将同时使用 Windows 命令提示符和 Git Bash 提示符。

  1. 在 Azure 门户中,选择设备预配服务的“概述”选项卡。

  2. 复制“ID 范围”。

    Azure 门户中的 ID 范围的屏幕截图。

  3. 在 Windows 命令提示符下,导航到示例项目文件夹。 显示的路径相对于 SDK 克隆到的位置

    cd .\azure-iot-sdk-java\provisioning\provisioning-device-client-samples\provisioning-X509-sample
    
  4. 在示例代码中输入预配服务和 X.509 标识信息。 在预配期间,该信息在注册设备之前用于证明模拟设备。

    1. 在你偏好的编辑器中打开文件 .\src\main\java\samples\com\microsoft\azure\sdk\iot\ProvisioningX509Sample.java

    2. 更新以下值。 对于 idScope,请使用先前复制的“ID 范围”。 对于全局终结点,请使用“全局设备终结点”。 此终结点对于每个 DPS 实例都是相同的:global.azure-devices-provisioning.net

      private static final String idScope = "[Your ID scope here]";
      private static final String globalEndpoint = "[Your Provisioning Service Global Endpoint here]";
      
    3. 该示例默认使用 HTTPS 作为传输协议。 如果你要更改协议,请注释掉以下行,并取消注释与你要使用的协议对应的行。

      private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.HTTPS;
      
    4. 使用设备证书 device-01.cert.pem 的值更新 leafPublicPem 常量字符串的值。

      证书文本的语法必须遵循下面的模式,并且不包含额外的空格或字符。

      private static final String leafPublicPem = "-----BEGIN CERTIFICATE-----\n"
      "MIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n"
          ...
      "MDMwWhcNMjAxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n"
      "-----END CERTIFICATE-----";        
      

      手动更新此字符串值可能容易出错。 若要生成正确的语法,可将以下命令复制并粘贴到 Git Bash 提示符下,然后按 ENTER。 此命令将生成 leafPublicPem 字符串常量值的语法并将其写入到输出。

      sed 's/^/"/;$ !s/$/\\n" +/;$ s/$/"/' ./certs/device-01.cert.pem
      

      复制并粘贴常量值的输出证书文本。

    5. 使用设备证书 unencrypted-device-key.pem 的未加密私钥更新 leafPrivateKey 常量的字符串值。

      私钥文本的语法必须遵循下面的模式,并且不包含额外的空格或字符。

      private static final String leafPrivateKey = "-----BEGIN PRIVATE KEY-----\n" +
      "MIIJJwIBAAKCAgEAtjvKQjIhp0EE1PoADL1rfF/W6v4vlAzOSifKSQsaPeebqg8U\n" +
          ...
      "X7fi9OZ26QpnkS5QjjPTYI/wwn0J9YAwNfKSlNeXTJDfJ+KpjXBcvaLxeBQbQhij\n" +
      "-----END PRIVATE KEY-----";
      

      若要生成正确的语法,可将以下命令复制并粘贴到 Git Bash 提示符下,然后按 ENTER。 此命令将生成 leafPrivateKey 字符串常量值的语法并将其写入到输出。

      sed 's/^/"/;$ !s/$/\\n" +/;$ s/$/"/' ./private/device-01.key.pem
      

      复制并粘贴常量值的输出私钥文本。

    6. 添加 rootPublicPem 常量字符串并在其中包含根 CA 证书 azure-iot-test-only.root.ca.cert.pem 的值。 可以在紧接在 leafPrivateKey 常量之后添加该字符串。

      证书文本的语法必须遵循下面的模式,并且不包含额外的空格或字符。

      private static final String rootPublicPem = "-----BEGIN CERTIFICATE-----\n"
      "MIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n"
          ...
      "MDMwWhcNMjAxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n"
      "-----END CERTIFICATE-----";        
      

      若要生成正确的语法,可将以下命令复制并粘贴到 Git Bash 提示符下,然后按 ENTER。 此命令将生成 rootPublicPem 字符串常量值的语法并将其写入到输出。

      sed 's/^/"/;$ !s/$/\\n" +/;$ s/$/"/' ./certs/azure-iot-test-only.root.ca.cert.pem
      

      复制并粘贴常量值的输出证书文本。

    7. 添加 intermediatePublicPem 常量字符串并在其中包含中间 CA 证书 azure-iot-test-only.intermediate.cert.pem 的值。 可以在紧接在上一个常量之后添加该字符串。

      证书文本的语法必须遵循下面的模式,并且不包含额外的空格或字符。

      private static final String intermediatePublicPem = "-----BEGIN CERTIFICATE-----\n"
      "MIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n"
          ...
      "MDMwWhcNMjAxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n"
      "-----END CERTIFICATE-----";        
      

      若要生成正确的语法,可将以下命令复制并粘贴到 Git Bash 提示符下,然后按 ENTER。 此命令将生成 intermediatePublicPem 字符串常量值的语法并将其写入到输出。

      sed 's/^/"/;$ !s/$/\\n" +/;$ s/$/"/' ./certs/azure-iot-test-only.intermediate.cert.pem
      

      复制并粘贴常量值的输出证书文本。

    8. main 方法中找到以下行。

      // For group enrollment uncomment this line
      //signerCertificatePemList.add("<Your Signer/intermediate Certificate Here>");
      

      紧接在这些行的下面添加以下两行,以将中间 CA 证书和根 CA 证书添加到签名链中。 签名链应包括整个证书链,直至包括已使用 DPS 验证的证书。

      signerCertificatePemList.add(intermediatePublicPem);
      signerCertificatePemList.add(rootPublicPem);
      

      注意

      签名证书的添加顺序非常重要。 如果更改了顺序,该示例将会失败。

    9. 保存所做更改。

  5. 生成示例,然后转到 target 文件夹。

    mvn clean install
    cd target
    
  6. 生成过程将在 target 文件夹中输出采用以下文件格式的 .jar 文件:provisioning-x509-sample-{version}-with-deps.jar;例如:provisioning-x509-sample-1.8.1-with-deps.jar。 执行该 .jar 文件。 可能需要替换以下命令中的版本。

    java -jar ./provisioning-x509-sample-1.8.1-with-deps.jar
    

    该示例将连接到 DPS,后者会将设备预配到 IoT 中心。 预配设备后,示例将向 IoT 中心发送一些测试消息。

    Starting...
    Beginning setup.
    WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
    2022-10-21 10:41:20,476 DEBUG (main) [com.microsoft.azure.sdk.iot.provisioning.device.ProvisioningDeviceClient] - Initialized a ProvisioningDeviceClient instance using SDK version 2.0.2
    2022-10-21 10:41:20,479 DEBUG (main) [com.microsoft.azure.sdk.iot.provisioning.device.ProvisioningDeviceClient] - Starting provisioning thread...
    Waiting for Provisioning Service to register
    2022-10-21 10:41:20,482 INFO (global.azure-devices-provisioning.net-4f8279ac-CxnPendingConnectionId-azure-iot-sdk-ProvisioningTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask] - Opening the connection to device provisioning service...
    2022-10-21 10:41:20,652 INFO (global.azure-devices-provisioning.net-4f8279ac-Cxn4f8279ac-azure-iot-sdk-ProvisioningTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask] - Connection to device provisioning service opened successfully, sending initial device registration message
    2022-10-21 10:41:20,680 INFO (global.azure-devices-provisioning.net-4f8279ac-Cxn4f8279ac-azure-iot-sdk-RegisterTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.RegisterTask] - Authenticating with device provisioning service using x509 certificates
    2022-10-21 10:41:21,603 INFO (global.azure-devices-provisioning.net-4f8279ac-Cxn4f8279ac-azure-iot-sdk-ProvisioningTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask] - Waiting for device provisioning service to provision this device...
    2022-10-21 10:41:21,605 INFO (global.azure-devices-provisioning.net-4f8279ac-Cxn4f8279ac-azure-iot-sdk-ProvisioningTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask] - Current provisioning status: ASSIGNING
    2022-10-21 10:41:24,868 INFO (global.azure-devices-provisioning.net-4f8279ac-Cxn4f8279ac-azure-iot-sdk-ProvisioningTask) [com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask] - Device provisioning service assigned the device successfully
    IotHUb Uri : contoso-hub-2.azure-devices.net
    Device ID : device-01
    2022-10-21 10:41:30,514 INFO (main) [com.microsoft.azure.sdk.iot.device.transport.ExponentialBackoffWithJitter] - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
    2022-10-21 10:41:30,526 INFO (main) [com.microsoft.azure.sdk.iot.device.transport.ExponentialBackoffWithJitter] - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
    2022-10-21 10:41:30,533 DEBUG (main) [com.microsoft.azure.sdk.iot.device.DeviceClient] - Initialized a DeviceClient instance using SDK version 2.1.2
    2022-10-21 10:41:30,590 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.MqttIotHubConnection] - Opening MQTT connection...
    2022-10-21 10:41:30,625 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.Mqtt] - Sending MQTT CONNECT packet...
    2022-10-21 10:41:31,452 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.Mqtt] - Sent MQTT CONNECT packet was acknowledged
    2022-10-21 10:41:31,453 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.Mqtt] - Sending MQTT SUBSCRIBE packet for topic devices/device-01/messages/devicebound/#
    2022-10-21 10:41:31,523 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.Mqtt] - Sent MQTT SUBSCRIBE packet for topic devices/device-01/messages/devicebound/# was acknowledged
    2022-10-21 10:41:31,525 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.mqtt.MqttIotHubConnection] - MQTT connection opened successfully
    2022-10-21 10:41:31,528 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - The connection to the IoT Hub has been established
    2022-10-21 10:41:31,531 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Updating transport status to new status CONNECTED with reason CONNECTION_OK
    2022-10-21 10:41:31,532 DEBUG (main) [com.microsoft.azure.sdk.iot.device.DeviceIO] - Starting worker threads
    2022-10-21 10:41:31,535 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Invoking connection status callbacks with new status details
    2022-10-21 10:41:31,536 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Client connection opened successfully
    2022-10-21 10:41:31,537 INFO (main) [com.microsoft.azure.sdk.iot.device.DeviceClient] - Device client opened successfully
    Sending message from device to IoT Hub...
    2022-10-21 10:41:31,539 DEBUG (main) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Message was queued to be sent later ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] )
    Press any key to exit...
    2022-10-21 10:41:31,540 DEBUG (contoso-hub-2.azure-devices.net-device-01-d7c67552-Cxn0bd73809-420e-46fe-91ee-942520b775db-azure-iot-sdk-IotHubSendTask) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Sending message ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] )
    2022-10-21 10:41:31,844 DEBUG (MQTT Call: device-01) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - IotHub message was acknowledged. Checking if there is record of sending this message ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] )
    2022-10-21 10:41:31,846 DEBUG (contoso-hub-2.azure-devices.net-device-01-d7c67552-Cxn0bd73809-420e-46fe-91ee-942520b775db-azure-iot-sdk-IotHubSendTask) [com.microsoft.azure.sdk.iot.device.transport.IotHubTransport] - Invoking the callback function for sent message, IoT Hub responded to message ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] ) with status OK
    Message sent!
    
  7. 根据下表更新第二台设备 (device-02) 的常量,重新生成并再次运行该示例。

    返回的常量 要使用的文件
    leafPublicPem ./certs/device-02.cert.pem
    leafPrivateKey ./private/device-02.key.pem

确认设备预配注册

查看注册组的注册记录以了解设备的注册详细信息:

  1. 在 Azure 门户中转到设备预配服务实例。

  2. 在“设置”菜单中,选择“管理注册” 。

  3. 选择“注册组”。 之前创建的 X.509 注册组条目应显示在列表中。

  4. 选择该注册条目。 然后选择“注册状态”旁边的“详细信息”,以查看已通过注册组注册的设备。 每台设备分配到的 IoT 中心、设备 ID 以及设备注册日期和时间会显示在列表中。

    屏幕截图显示 Azure 门户中注册组的注册状态详细信息。

  5. 可以选择其中一台设备以查看该设备的更多详细信息。

若要验证 IoT 中心上的设备,请执行以下操作:

  1. 在 Azure 门户中,转到设备分配到的 IoT 中心。

  2. 在“设备管理”菜单中选择“设备”。

  3. 如果设备已成功预配,则其设备 ID device-01 和 device-02 应显示在列表中,且“状态”设置为“已启用”。 如果未看到你的设备,请选择“刷新”。

    显示设备已注册到 Azure 门户的 IoT 中心的屏幕截图。

清理资源

完成此设备客户端示例的测试和研究后,使用以下步骤删除本教程创建的所有资源。

  1. 关闭计算机上的设备客户端示例输出窗口。

删除注册组

  1. 在 Azure 门户的左侧菜单中,选择“所有资源”。

  2. 选择 DPS 实例。

  3. 在“设置”菜单中,选择“管理注册” 。

  4. 选择“注册组”选项卡。

  5. 选择在本教程中使用的注册组。

  6. 在“注册详细信息”页上,选择“注册状态”旁边的“详细信息”。 然后选中“设备 ID”列标题旁边的复选框,以选择注册组的所有注册记录。 选择页面顶部的“删除”以删除注册记录。

    重要

    删除注册组不会删除与之关联的注册记录。 这些孤立记录将计入 DPS 实例的注册配额。 因此,最好先删除与注册组关联的所有注册记录,然后再删除注册组本身。

  7. 返回“管理注册”页,并确保选择了“注册组”选项卡。

  8. 选中在本教程中使用的注册组组名旁边的复选框。

  9. 在页面顶部,选择“删除”。

从 DPS 中删除已注册的 CA 证书

  1. 在 DPS 实例的左侧菜单中选择“证书”。 对于在本教程中上传和验证的每个证书,请选择该证书,然后选择“删除”并确认将其删除。

从 IoT 中心删除设备注册

  1. 在 Azure 门户的左侧菜单中,选择“所有资源”。

  2. 选择 IoT 中心。

  3. 在“资源管理器”菜单中选择“IoT 设备” 。

  4. 选中在本教程中注册的设备的设备 ID 旁边的复选框。 例如 device-01 和 device-02。

  5. 在页面顶部,选择“删除”。

后续步骤

在本教程中,你已使用注册组将多个 X.509 设备预配到 IoT 中心。 接下来了解如何跨多个中心预配 IoT 设备。