如何將 IoT Central 應用程式與具有 x.509 憑證的裝置連線

IoT Central 支援 (SAS) 和 X.509 憑證的共用存取簽章,以保護裝置與應用程式之間的通訊。 建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程使用 SAS。 在本文中,您將了解如何修改程式碼範例以使用 X.509 憑證。 生產環境中建議使用 X.509 憑證。 如需詳細資訊,請參閱裝置驗證概念

本指南示範兩種使用 X.509 憑證的方式 - 群組註冊 通常用於生產環境中,以及適合進行測試的個別註冊。 本文也說明如何復原裝置憑證,以在憑證到期時維持連線能力。

本指南是以使用 C#、JAVA、JavaScript 和 Python 的建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程中所顯示的範例為基礎。 如需使用 C 程式設計語言的範例,請參閱使用註冊群組佈建多個 X.509 裝置

必要條件

若要完成本操作指南中的步驟,您應該先完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程。

在此操作指南中,您會產生一些測試 X.509 憑證。 若要能夠產生這些憑證,您需要:

  • 已安裝 Node.js 版本 6 或更新版本的開發電腦。 您可以在命令列執行 node --version 來檢查版本。 本教學課程中的指示假設您是在 Windows 命令提示字元中執行 node 命令。 不過,您可以在許多其他作業系統上使用 Node.js。
  • 適用於 Node.js 的 Microsoft Azure IoT SDK GitHub 存放庫的本機複本,其中包含用來產生測試 X.509 憑證的指令碼。 使用此連結來下載存放庫的複本:下載 ZIP。 接著將檔案解壓縮到本機電腦上的適當位置。

使用群組註冊

在生產環境中使用群組註冊的 X.509 憑證。 在群組註冊中,您會將根或中繼 X.509 憑證新增至 IoT Central 應用程式。 分葉憑證衍生自根或中繼憑證的裝置可以連線到您的應用程式。

產生根憑證和裝置憑證

在本節中,您會使用 X.509 憑證,將裝置與衍生自 IoT Central 註冊群組憑證的憑證連線。

警告

這種產生 X.509 憑證的方式僅供測試使用。 若為生產環境,請使用官方的安全機制來產生憑證。

  1. 瀏覽至您下載的適用於 Node.js 的 Microsoft Azure IoT SDK 中的憑證產生器指令碼。 安裝必要的套件:

    cd azure-iot-sdk-node/provisioning/tools
    npm install
    
  2. 建立根憑證,然後執行指令碼來衍生裝置憑證:

    node create_test_cert.js root mytestrootcert
    node create_test_cert.js device sample-device-01 mytestrootcert
    

    提示

    裝置識別碼可以包含字母、數字和 - 字元。

這些命令會產生下列根憑證和裝置憑證

filename 內容
mytestrootcert_cert.pem X509 根憑證的公用部分
mytestrootcert_key.pem X509 根憑證的私密金鑰
mytestrootcert_fullchain.pem X509 根憑證的整個金鑰鏈。
mytestrootcert.pfx X509 根憑證的 PFX 檔案。
sampleDevice01_cert.pem X509 裝置憑證的公用部分
sampleDevice01_key.pem X509 裝置憑證的私密金鑰
sampleDevice01_fullchain.pem X509 裝置憑證的整個金鑰鏈。
sampleDevice01.pfx X509 裝置憑證的 PFX 檔案。

請記下這些檔案的位置。 您稍後將會用到此資訊。

建立群組註冊

  1. 開啟 IoT Central 應用程式,然後瀏覽至左窗格中的 [權限],然後選取 [裝置連線群組]。

  2. 選取 [+ 新增] 以建立名為 MyX509Group 的新註冊群組,其證明類型為 [憑證 (X.509)]。 您可以為 IoT 裝置或IoT Edge裝置建立註冊群組。

  3. 在您建立的註冊群組中,選取 [管理主要憑證]。

  4. 在 [主要憑證] 面板中,選取 [新增憑證]。

  5. 上傳您先前產生的 mytestrootcert_cert.pem 根憑證檔案。

  6. 如果您使用信任的中繼或根憑證授權單位,並知道您擁有憑證的完整所有權,您可以透過將上傳時驗證的憑證狀態設定為 [開啟],來自我證明您已驗證憑證。 否則,請將上傳時驗證的憑證狀態設定為 [關閉]。

  7. 如果您將上傳時驗證的憑證狀態設定為 [關閉],請選取 [產生驗證碼]。

  8. 複製驗證碼,複製後建立 X.509 驗證憑證。 例如,在命令提示字元下:

    node create_test_cert.js verification --ca mytestrootcert_cert.pem --key mytestrootcert_key.pem --nonce  {verification-code}
    
  9. 選取 [驗證] 以上傳已簽署的驗證憑證 verification_cert.pem,即可完成驗證。

  10. 主要憑證的狀態現在已驗證

    顯示已驗證 X509 憑證的螢幕擷取畫面。

您現在可以連接具有衍生自此主要根憑證的 X.509 憑證的裝置。

儲存註冊群組之後,請記下識別碼範圍。

執行範例裝置程式碼

如果您使用 Windows,X.509 憑證必須放置在 Windows 憑證存放區中,範例才能運作。 在 Windows 檔案總管中,按兩下您先前產生的 PFX 檔案 - mytestrootcert.pfxsampleDevice01.pfx。 在 [憑證匯入精靈] 中,選取 [目前使用者] 作為存放區位置,輸入 1234 作為密碼,然後讓精靈自動選擇憑證存放區。 精靈會將憑證匯入目前使用者的個人存放區。

若要修改範例程式碼以使用 X.509 憑證:

  1. IoTHubDeviceSamples Visual Studio 方案中,開啟 TemperatureController 專案中的 Parameter.cs 檔案。

  2. 將以下兩個參數定義新增至類別:

    [Option(
        'x',
        "CertificatePath",
        HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device PFX file to use during device provisioning." +
        "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_CERT\".")]
    public string CertificatePath { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_CERT");
    
    [Option(
        'p',
        "CertificatePassword",
        HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe password of the PFX certificate file." +
        "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_PASSWORD\".")]
    public string CertificatePassword { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_PASSWORD");
    

    儲存變更。

  3. IoTHubDeviceSamples Visual Studio 方案中,開啟 TemperatureController 專案中的 Program.cs 檔案。

  4. 新增下列 using 陳述式:

    using System.Security.Cryptography.X509Certificates;
    using System.IO;
    
  5. 將下列方法加入至類別:

    private static X509Certificate2 LoadProvisioningCertificate(Parameters parameters)
    {
        var certificateCollection = new X509Certificate2Collection();
        certificateCollection.Import(
            parameters.CertificatePath,
            parameters.CertificatePassword,
            X509KeyStorageFlags.UserKeySet);
    
        X509Certificate2 certificate = null;
    
        foreach (X509Certificate2 element in certificateCollection)
        {
            Console.WriteLine($"Found certificate: {element?.Thumbprint} {element?.Subject}; PrivateKey: {element?.HasPrivateKey}");
            if (certificate == null && element.HasPrivateKey)
            {
                certificate = element;
            }
            else
            {
                element.Dispose();
            }
        }
    
        if (certificate == null)
        {
            throw new FileNotFoundException($"{parameters.CertificatePath} did not contain any certificate with a private key.");
        }
    
        Console.WriteLine($"Using certificate {certificate.Thumbprint} {certificate.Subject}");
    
        return certificate;
    }
    
  6. SetupDeviceClientAsync 方法中,將 case "dps" 的程式碼區塊取代為下列程式碼:

    case "dps":
        s_logger.LogDebug($"Initializing via DPS");
        Console.WriteLine($"Loading the certificate...");
        X509Certificate2 certificate = LoadProvisioningCertificate(parameters);
        DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, certificate, cancellationToken);
        var authMethod = new DeviceAuthenticationWithX509Certificate(dpsRegistrationResult.DeviceId, certificate);
        deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
        break;
    
  7. 以下列程式碼取代 ProvisionDeviceAsync 方法:

    private static async Task<DeviceRegistrationResult> ProvisionDeviceAsync(Parameters parameters, X509Certificate2 certificate, CancellationToken cancellationToken)
    {
        SecurityProvider security = new SecurityProviderX509Certificate(certificate);
        ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt();
        ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, security, mqttTransportHandler);
    
        var pnpPayload = new ProvisioningRegistrationAdditionalData
        {
            JsonData = PnpConvention.CreateDpsPayload(ModelId),
        };
        return await pdc.RegisterAsync(pnpPayload, cancellationToken);
    }
    

    儲存變更。

若要執行範例:

  1. 將下列環境變數新增至專案:

    • IOTHUB_DEVICE_X509_CERT: <full path to folder that contains PFX files>sampleDevice01.pfx
    • IOTHUB_DEVICE_X509_PASSWORD: 1234.
  2. 建置並執行應用程式。 確認裝置佈建成功。

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至 azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample 資料夾,其中包含溫度控制器裝置範例的 pom.xml 檔案和 src 資料夾。

  2. 編輯 pom.xml 檔案,在 <dependencies> 節點中新增下列相依性設定:

    <dependency>
        <groupId>com.microsoft.azure.sdk.iot.provisioning.security</groupId>
        <artifactId>${x509-provider-artifact-id}</artifactId>
        <version>${x509-provider-version}</version>
    </dependency>
    

    儲存變更。

  3. 在文字編輯器中開啟 src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java 檔案。

  4. 以下列匯入項目取代 SecurityProviderSymmetricKey 匯入項目:

    import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider;
    import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert;
    import com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException;
    
  5. 請新增下列匯入項目:

    import java.nio.file.*;
    
  6. SecurityProviderException 新增至 main 方法擲回的例外狀況清單:

    public static void main(String[] args) throws IOException, URISyntaxException, ProvisioningDeviceClientException, InterruptedException, SecurityProviderException {
    
  7. 以下列程式碼取代 initializeAndProvisionDevice 方法:

    private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, SecurityProviderException {
        String deviceX509Key = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_KEY"))));
        String deviceX509Cert = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_CERT"))));
        SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(deviceX509Cert, deviceX509Key, null);
        ProvisioningDeviceClient provisioningDeviceClient;
        ProvisioningStatus provisioningStatus = new ProvisioningStatus();
    
        provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityProviderX509);
    
        AdditionalData additionalData = new AdditionalData();
        additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));
    
        provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus, additionalData);
    
        while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
        {
            if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR ||
                    provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED ||
                    provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED)
            {
                provisioningStatus.exception.printStackTrace();
                System.out.println("Registration error, bailing out");
                break;
            }
            System.out.println("Waiting for Provisioning Service to register");
            Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
        }
    
        ClientOptions options = new ClientOptions();
        options.setModelId(MODEL_ID);
    
        if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) {
            System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri());
            System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId());
    
            String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri();
            String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId();
    
            log.debug("Opening the device client.");
            deviceClient = DeviceClient.createFromSecurityProvider(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT, options);
            deviceClient.open();
        }
    }
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 建置並執行應用程式。 確認裝置佈建成功。

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至包含 pnp_temperature_controller.js 應用程式的 azure-iot-sdk-node/device/samples/javascript 資料夾,然後執行下列命令來安裝 X.509 套件:

    npm install azure-iot-security-x509 --save
    
  2. 在文字編輯器中開啟 pnp_temperature_controller.js 檔案。

  3. 編輯 require 陳述式以包含下列程式碼:

    const fs = require('fs');
    const X509Security = require('azure-iot-security-x509').X509Security;
    
  4. 將下列四行內容新增至「DPS 連線資訊」區段,以初始化 deviceCert 變數:

    const deviceCert = {
      cert: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_CERT).toString(),
      key: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_KEY).toString()
    };
    
  5. 使用下列程式碼取代第一行,編輯建立用戶端的 provisionDevice 函式:

    var provSecurityClient = new X509Security(registrationId, deviceCert);
    
  6. 在相同的函式中,修改設定 deviceConnectionString 變數的行,如下所示:

    deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';x509=true';
    
  7. main 函式中,在呼叫 Client.fromConnectionString 的行之後新增下列程式碼行:

    client.setOptions(deviceCert);
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 執行指令碼,並確認裝置已成功佈建:

    node pnp_temperature_controller.js
    

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至 azure-iot-device/samples/pnp 資料夾,然後在文字編輯器中開啟 temp_controller_with_thermostats.py 檔案。

  2. 新增下列 from 陳述式以匯入 X.509 功能:

    from azure.iot.device import X509
    
  3. 修改 provision_device 函式的第一個部分,如下所示:

    async def provision_device(provisioning_host, id_scope, registration_id, x509, model_id):
        provisioning_device_client = ProvisioningDeviceClient.create_from_x509_certificate(
            provisioning_host=provisioning_host,
            registration_id=registration_id,
            id_scope=id_scope,
            x509=x509,
        )
    
  4. main 函式中,以下列程式碼取代設定 symmetric_key 變數的行:

    x509 = X509(
        cert_file=os.getenv("IOTHUB_DEVICE_X509_CERT"),
        key_file=os.getenv("IOTHUB_DEVICE_X509_KEY"),
    )
    
  5. main 函式中,以下列程式碼取代對 provision_device 函式的呼叫:

    registration_result = await provision_device(
        provisioning_host, id_scope, registration_id, x509, model_id
    )
    
  6. main 函式中,以下列程式碼取代對 IoTHubDeviceClient.create_from_symmetric_key 函式的呼叫:

    device_client = IoTHubDeviceClient.create_from_x509_certificate(
        x509=x509,
        hostname=registration_result.registration_state.assigned_hub,
        device_id=registration_result.registration_state.device_id,
        product_info=model_id,
    )
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>sampleDevice01_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>sampleDevice01_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 執行指令碼,並確認裝置已成功佈建:

    python temp_controller_with_thermostats.py
    

確認遙測顯示在 IoT Central 應用程式中的裝置檢視上:

螢幕擷取畫面顯示使用 X.509 連線的裝置遙測。

使用個別註冊

使用個別註冊的 X.509 憑證來測試您的裝置和解決方案。 在個別註冊中,IoT Central 應用程式中沒有根或中繼 X.509 憑證。 裝置會使用自我簽署的 X.509 憑證連線到您的應用程式。

建立自我簽署裝置憑證

在本節中,您會使用自我簽署的 X.509 憑證來連接用於註冊單一裝置的裝置。 自我簽署的憑證僅供測試。

警告

這種產生 X.509 憑證的方式僅供測試使用。 若為生產環境,請使用官方的安全機制來產生憑證。

執行下列命令來建立自我簽署 X.509 裝置憑證:

  cd azure-iot-sdk-node/provisioning/tools
  node create_test_cert.js device mytestselfcertprimary
  node create_test_cert.js device mytestselfcertsecondary 

提示

裝置識別碼可以包含字母、數字和 - 字元。

這些命令會產生下列裝置憑證:

filename 內容
mytestselfcertprimary_cert.pem 主要裝置 X509 憑證的公用部分
mytestselfcertprimary_key.pem 主要裝置 X509 憑證的私密金鑰
mytestselfcertprimary_fullchain.pem 主要裝置 X509 憑證的整個金鑰鏈。
mytestselfcertprimary.pfx 主要裝置 X509 憑證的 PFX 檔案。
mytestselfcertsecondary_cert.pem 次要裝置 X509 憑證的公用部分
mytestselfcertsecondary_key.pem 次要裝置 X509 憑證的私密金鑰
mytestselfcertsecondary_fullchain.pem 次要裝置 X509 憑證的整個金鑰鏈。
mytestselfcertsecondary.pfx 次要裝置 X509 憑證的 PFX 檔案。

建立個別註冊

  1. 在 Azure IoT Central 應用程式中,選取 [裝置],然後從控溫器裝置範本建立 [裝置識別碼] 為 mytestselfcertprimary 的新裝置。 記下識別碼範圍,您稍後會使用此值。

  2. 開啟您建立的裝置,然後選取 [連線]。

  3. 選取 [個別註冊] 作為 [驗證類型] 和 [憑證 (X.509)] 作為 [驗證方法]。

  4. 上傳您先前產生為主要憑證的 mytestselfcertprimary_cert.pem 檔案。

  5. 上傳您先前產生為此要憑證的 mytestselfcertsecondary_cert.pem 檔案。 然後選取 [儲存]。

  6. 裝置現在具有的個別註冊具有 X.509 憑證。

    顯示如何使用 X.509 個別註冊來連線裝置的螢幕擷取畫面。

執行範例個別註冊裝置

如果您使用 Windows,X.509 憑證必須放置在 Windows 憑證存放區中,範例才能運作。 在 Windows 檔案總管中,按兩下您先前產生的 PFX 檔案 - mytestselfcertprimary.pfxmytestselfcertsecondary.pfx。 在 [憑證匯入精靈] 中,選取 [目前使用者] 作為存放區位置,輸入 1234 作為密碼,然後讓精靈自動選擇憑證存放區。 精靈會將憑證匯入目前使用者的個人存放區。

若要修改範例程式碼以使用 X.509 憑證:

  1. IoTHubDeviceSamples Visual Studio 方案中,開啟 TemperatureController 專案中的 Parameter.cs 檔案。

  2. 將以下兩個參數定義新增至類別:

    [Option(
        'x',
        "CertificatePath",
        HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device PFX file to use during device provisioning." +
        "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_CERT\".")]
    public string CertificatePath { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_CERT");
    
    [Option(
        'p',
        "CertificatePassword",
        HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe password of the PFX certificate file." +
        "\nDefaults to environment variable \"IOTHUB_DEVICE_X509_PASSWORD\".")]
    public string CertificatePassword { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_X509_PASSWORD");
    

    儲存變更。

  3. IoTHubDeviceSamples Visual Studio 方案中,開啟 TemperatureController 專案中的 Program.cs 檔案。

  4. 新增下列 using 陳述式:

    using System.Security.Cryptography.X509Certificates;
    using System.IO;
    
  5. 將下列方法加入至類別:

    private static X509Certificate2 LoadProvisioningCertificate(Parameters parameters)
    {
        var certificateCollection = new X509Certificate2Collection();
        certificateCollection.Import(
            parameters.CertificatePath,
            parameters.CertificatePassword,
            X509KeyStorageFlags.UserKeySet);
    
        X509Certificate2 certificate = null;
    
        foreach (X509Certificate2 element in certificateCollection)
        {
            Console.WriteLine($"Found certificate: {element?.Thumbprint} {element?.Subject}; PrivateKey: {element?.HasPrivateKey}");
            if (certificate == null && element.HasPrivateKey)
            {
                certificate = element;
            }
            else
            {
                element.Dispose();
            }
        }
    
        if (certificate == null)
        {
            throw new FileNotFoundException($"{parameters.CertificatePath} did not contain any certificate with a private key.");
        }
    
        Console.WriteLine($"Using certificate {certificate.Thumbprint} {certificate.Subject}");
    
        return certificate;
    }
    
  6. SetupDeviceClientAsync 方法中,將 case "dps" 的程式碼區塊取代為下列程式碼:

    case "dps":
        s_logger.LogDebug($"Initializing via DPS");
        Console.WriteLine($"Loading the certificate...");
        X509Certificate2 certificate = LoadProvisioningCertificate(parameters);
        DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, certificate, cancellationToken);
        var authMethod = new DeviceAuthenticationWithX509Certificate(dpsRegistrationResult.DeviceId, certificate);
        deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
        break;
    
  7. 以下列程式碼取代 ProvisionDeviceAsync 方法:

    private static async Task<DeviceRegistrationResult> ProvisionDeviceAsync(Parameters parameters, X509Certificate2 certificate, CancellationToken cancellationToken)
    {
        SecurityProvider security = new SecurityProviderX509Certificate(certificate);
        ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt();
        ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, security, mqttTransportHandler);
    
        var pnpPayload = new ProvisioningRegistrationAdditionalData
        {
            JsonData = PnpConvention.CreateDpsPayload(ModelId),
        };
        return await pdc.RegisterAsync(pnpPayload, cancellationToken);
    }
    

    儲存變更。

若要執行範例:

  1. 將下列環境變數新增至專案:

    • IOTHUB_DEVICE_DPS_DEVICE_ID:mytestselfcertprimary
    • IOTHUB_DEVICE_X509_CERT: <full path to folder that contains PFX files>mytestselfcertprimary.pfx
    • IOTHUB_DEVICE_X509_PASSWORD: 1234.
  2. 建置並執行應用程式。 確認裝置佈建成功。

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至 azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample 資料夾,其中包含溫度控制器裝置範例的 pom.xml 檔案和 src 資料夾。

  2. 編輯 pom.xml 檔案,在 <dependencies> 節點中新增下列相依性設定:

    <dependency>
        <groupId>com.microsoft.azure.sdk.iot.provisioning.security</groupId>
        <artifactId>${x509-provider-artifact-id}</artifactId>
        <version>${x509-provider-version}</version>
    </dependency>
    

    儲存變更。

  3. 在文字編輯器中開啟 src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java 檔案。

  4. 以下列匯入項目取代 SecurityProviderSymmetricKey 匯入項目:

    import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider;
    import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert;
    import com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException;
    
  5. 請新增下列匯入項目:

    import java.nio.file.*;
    
  6. SecurityProviderException 新增至 main 方法擲回的例外狀況清單:

    public static void main(String[] args) throws IOException, URISyntaxException, ProvisioningDeviceClientException, InterruptedException, SecurityProviderException {
    
  7. 以下列程式碼取代 initializeAndProvisionDevice 方法:

    private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, SecurityProviderException {
        String deviceX509Key = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_KEY"))));
        String deviceX509Cert = new String(Files.readAllBytes(Paths.get(System.getenv("IOTHUB_DEVICE_X509_CERT"))));
        SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(deviceX509Cert, deviceX509Key, null);
        ProvisioningDeviceClient provisioningDeviceClient;
        ProvisioningStatus provisioningStatus = new ProvisioningStatus();
    
        provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityProviderX509);
    
        AdditionalData additionalData = new AdditionalData();
        additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));
    
        provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus, additionalData);
    
        while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
        {
            if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR ||
                    provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED ||
                    provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED)
            {
                provisioningStatus.exception.printStackTrace();
                System.out.println("Registration error, bailing out");
                break;
            }
            System.out.println("Waiting for Provisioning Service to register");
            Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
        }
    
        ClientOptions options = new ClientOptions();
        options.setModelId(MODEL_ID);
    
        if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) {
            System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri());
            System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId());
    
            String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri();
            String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId();
    
            log.debug("Opening the device client.");
            deviceClient = DeviceClient.createFromSecurityProvider(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT, options);
            deviceClient.open();
        }
    }
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary
    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 建置並執行應用程式。 確認裝置佈建成功。

您也可以針對 mytestselfcertsecondary 憑證重複上述步驟。

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至包含 pnp_temperature_controller.js 應用程式的 azure-iot-sdk-node/device/samples/javascript 資料夾,然後執行下列命令來安裝 X.509 套件:

    npm install azure-iot-security-x509 --save
    
  2. 在文字編輯器中開啟 pnp_temperature_controller.js 檔案。

  3. 編輯 require 陳述式以包含下列程式碼:

    const fs = require('fs');
    const X509Security = require('azure-iot-security-x509').X509Security;
    
  4. 將下列四行內容新增至「DPS 連線資訊」區段,以初始化 deviceCert 變數:

    const deviceCert = {
      cert: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_CERT).toString(),
      key: fs.readFileSync(process.env.IOTHUB_DEVICE_X509_KEY).toString()
    };
    
  5. 使用下列程式碼取代第一行,編輯建立用戶端的 provisionDevice 函式:

    var provSecurityClient = new X509Security(registrationId, deviceCert);
    
  6. 在相同的函式中,修改設定 deviceConnectionString 變數的行,如下所示:

    deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';x509=true';
    
  7. main 函式中,在呼叫 Client.fromConnectionString 的行之後新增下列程式碼行:

    client.setOptions(deviceCert);
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary
    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 執行指令碼,並確認裝置已成功佈建:

    node pnp_temperature_controller.js
    

您也可以針對 mytestselfcertsecondary 憑證重複上述步驟。

若要修改範例程式碼以使用 X.509 憑證:

  1. 瀏覽至 azure-iot-device/samples/pnp 資料夾,然後在文字編輯器中開啟 temp_controller_with_thermostats.py 檔案。

  2. 新增下列 from 陳述式以匯入 X.509 功能:

    from azure.iot.device import X509
    
  3. 修改 provision_device 函式的第一個部分,如下所示:

    async def provision_device(provisioning_host, id_scope, registration_id, x509, model_id):
        provisioning_device_client = ProvisioningDeviceClient.create_from_x509_certificate(
            provisioning_host=provisioning_host,
            registration_id=registration_id,
            id_scope=id_scope,
            x509=x509,
        )
    
  4. main 函式中,以下列程式碼取代設定 symmetric_key 變數的行:

    x509 = X509(
        cert_file=os.getenv("IOTHUB_DEVICE_X509_CERT"),
        key_file=os.getenv("IOTHUB_DEVICE_X509_KEY"),
    )
    
  5. main 函式中,以下列程式碼取代對 provision_device 函式的呼叫:

    registration_result = await provision_device(
        provisioning_host, id_scope, registration_id, x509, model_id
    )
    
  6. main 函式中,以下列程式碼取代對 IoTHubDeviceClient.create_from_symmetric_key 函式的呼叫:

    device_client = IoTHubDeviceClient.create_from_x509_certificate(
        x509=x509,
        hostname=registration_result.registration_state.assigned_hub,
        device_id=registration_result.registration_state.device_id,
        product_info=model_id,
    )
    

    儲存變更。

若要執行範例:

  1. 在您的殼層環境中,新增下列兩個環境變數。 請確定提供 PEM 檔案的完整路徑,並使用作業系統的正確路徑分隔符號:

    set IOTHUB_DEVICE_DPS_DEVICE_ID=mytestselfcertprimary
    set IOTHUB_DEVICE_X509_CERT=<full path to folder that contains PEM files>mytestselfcertprimary_cert.pem
    set IOTHUB_DEVICE_X509_KEY=<full path to folder that contains PEM files>mytestselfcertprimary_key.pem
    

    提示

    當您完成建立用戶端應用程式並連線至 Azure IoT Central 應用程式教學課程時,您會設定其他必要的環境變數。

  2. 執行指令碼,並確認裝置已成功佈建:

    python temp_controller_with_thermostats.py
    

您也可以針對 mytestselfcertsecondary 憑證重複上述步驟。

連線到 IoT Edge 裝置

本節假設您使用群組註冊來連線 IoT Edge 裝置。 遵循上一節的步驟以完成下列事項:

若要使用 X.509 裝置憑證將 IoT Edge 裝置連線到 IoT Central:

  • 將裝置憑證和金鑰檔案複製到您的 IoT Edge 裝置。 在先前的群組註冊範例中,這些檔案名為 sampleDevice01_key.pemsampleDevice01_cert.pem

  • 在IoT Edge裝置上,編輯 provisioning/etc/aziot/config.toml組態檔中的區段,如下所示:

    # DPS X.509 provisioning configuration
    provisioning:
      source: "dps"
      global_endpoint: "https://global.azure-devices-provisioning.net"
      scope_id: "<SCOPE_ID>"
      attestation:
        method: "x509"
    #   registration_id: "<OPTIONAL REGISTRATION ID. LEAVE COMMENTED OUT TO REGISTER WITH CN OF identity_cert>"
        identity_cert: "file:///<path>/sampleDevice01_cert.pem"
        identity_pk: "file:///<path>/sampleDevice01_key.pem"
    #  always_reprovision_on_startup: true
    #  dynamic_reprovisioning: false
    
    [provisioning]
    source = "dps"
    global_endpoint = "https://global.azure-devices-provisioning.net"
    id_scope = "<SCOPE_ID>"
    
    [provisioning.attestation]
    method = "x509"
    registration_id = "env-sens-001"
    identity_pk = "file:///<path>/envSens001_key.pem"
    identity_cert = "file:///<path>/envSens001_cert.pem"
    

    提示

    您不需要為 registration_id 新增值。 IoT Edge 可以使用 X.509 憑證中的 CN 值。

  • 執行下列命令以重新啟動 IoT Edge 執行階段:

    sudo iotedge config apply
    

若要深入了解,請參閱使用 X.509 憑證在 Linux 上大規模地建立和佈建 IoT Edge 裝置

將下游裝置連線到 IoT Edge

IoT Edge 使用 X.509 憑證來保護下游裝置與作為透明閘道的 IoT Edge裝置 之間的連線。 若要深入了解如何設定此案例,請參閱將下游裝置連線至 Azure IoT Edge 閘道

輪替 X.509 裝置憑證

在 IoT Central 應用程式的生命週期內,您必須輪替 x.509 憑證。 例如:

  • 若有安全性缺口,輪替憑證是一種最佳安全性做法,可協助保護您的系統。
  • x.509 憑證有到期日。 您輪替憑證的頻率取決於您解決方案的安全性需求。 解決方案涉及高度敏感性資料的客戶可以每天進行輪替,而其他客戶則可以每隔幾年輪替其憑證。

針對未中斷的連線,IoT Central 可讓您設定主要和次要 X.509 憑證。 如果主要和次要憑證有不同的到期日,您可以在裝置繼續與其他憑證連線時,輪替過期的憑證。

若要深入了解,請參閱假想缺口方法

本節說明如何在 IoT Central 中輪替憑證。 當您在 IoT Central 中輪替憑證時,也需要將新的裝置憑證複製到您的裝置。

取得新的 X.509 憑證

從您的憑證提供者取得新的 X.509 憑證。 您可以使用 OpenSSL 之類的工具,建立自己的 X.509 憑證。 這種方法適合用來測試 X.509 憑證,但是僅能提供少數的安全性保證。 除非您準備好作為自己的 CA 提供者,否則只能使用此方法進行測試。

註冊群組和安全性缺口

若要更新群組註冊以因應安全性缺口,則應使用下列方法立即更新目前的憑證。 如果主要和次要憑證都遭到入侵,則應對這兩者完成下列步驟:

  1. 瀏覽至左窗格中的 [權限],然後選取 [裝置連線群組]。

  2. 在 [註冊群組] 下,選取清單中的群組名稱。

  3. 針對憑證更新,選取 [管理主要憑證] 或 [管理次要憑證]。

  4. 在註冊群組中新增及驗證 X.509 根憑證。

個別註冊和安全性缺口

如果您是以輪替憑證的方式因應安全性缺口,請使用下列方法立即更新目前的憑證。 如果主要和次要憑證都遭到入侵,則應對這兩者完成下列步驟:

  1. 選取 [裝置],然後選取裝置。

  2. 選取 [連線],然後選取 [連線方法] 作為 [個別註冊]

  3. 選取 [憑證 (X.509)] 作為機制。

  4. 若要進行憑證更新,請選取資料夾圖示,以選取要針對註冊項目上傳的新憑證。 選取 [儲存]。

註冊群組與憑證到期

若要處理憑證到期,請使用下列方法來立即更新目前的憑證:

  1. 瀏覽至左窗格中的 [權限],然後選取 [裝置連線群組]。

  2. 在 [註冊群組] 下,選取清單中的群組名稱。

  3. 若要進行,請選取 [管理主要憑證]。

  4. 在註冊群組中新增及驗證 X.509 根憑證。

  5. 稍後當次要憑證過期時,請返回並更新次要憑證。

個別註冊和憑證到期

如果您是以輪替憑證的方式來處理憑證過期,則應使用如下所示的次要憑證組態,使嘗試在您應用程式佈建的裝置減少停機時間。

當次要憑證快要到期,而且需要輪替時,您可以輪換成使用主要組態。 如此一來,輪流使用主要與次要憑證可使嘗試在您應用程式佈建的裝置減少停機時間。

  1. 選取 [裝置],然後選取裝置。

  2. 選取 [連線],然後選取 [連線方法] 作為 [個別註冊]

  3. 選取 [憑證 (X.509)] 作為機制。

  4. 若要進行次要憑證更新,請選取資料夾圖示,以選取要針對註冊項目上傳的新憑證。 選取 [儲存]。

  5. 稍後當主要憑證過期時,請返回並更新主要憑證。

下一步

既然您已了解如何使用 X.509 憑證連線裝置,下一個步驟建議您了解如何使用 Azure CLI 監視裝置連線能力