X.509 証明書を使用するデバイスを Azure IoT Central アプリケーションに接続する方法

IoT Central では、デバイスとアプリケーションの間の通信をセキュリティで保護するために、共有アクセス署名 (SAS) と X.509 証明書の両方がサポートされています。 SAS は、「クライアント アプリケーションを作成して Azure IoT Central アプリケーションに接続する」チュートリアルで使用されます。 この記事では、X.509 証明書を使用するようにコード サンプルを変更する方法について説明します。 運用環境では、X.509 証明書はお勧めしません。 詳しくは、デバイスの認証の概念に関する記事をご覧ください。

このガイドでは、X.509 証明書の 2 つの使用方法を示します。通常は運用環境で使用されるグループ登録と、テストに役立つ個別登録です。 また、証明書の有効期限が切れても接続性を維持できるように、デバイス証明書を展開する方法についても説明します。

このガイドは、クライアント アプリケーションを作成して Azure IoT Central アプリケーションに接続するチュートリアルに示されている、C#、Java、JavaScript、Python を使用するサンプルに基づいています。 C プログラミング言語を使用した例については、登録グループを使って複数の X.509 デバイスをプロビジョニングするチュートリアルを参照してください。

前提条件

この攻略ガイドの手順を完了するには、まず、クライアント アプリケーションを作成して Azure IoT Central アプリケーションに接続する方法に関するチュートリアルを実施する必要があります。 このガイドの手順に従うときに、チュートリアルで使用したコードを変更します。

この攻略ガイドでは、テスト用の X.509 証明書を複数生成します。 これらの証明書を生成するには、次のものが必要です。

  • Node.js バージョン 6 以降がインストールされた開発用マシン。 バージョンを確認するには、コマンド ラインで node --version を実行できます。 このチュートリアルの手順は、Windows コマンド プロンプトから node コマンドを実行することを前提としています。 ただし、他の多くのオペレーティング システムで Node.js を使用できます。
  • テスト用の X.509 証明書を生成するスクリプトが格納された Microsoft Azure IoT SDK for Node.js GitHub リポジトリのローカル コピー。 このリンク (ZIP のダウンロード) を使用してリポジトリのコピーをダウンロードしてください。 次に、ローカル コンピューター上の適切な場所にファイルを解凍します。

グループ登録を使用する

運用環境のグループ登録では X.509 証明書を使用します。 グループ登録では、IoT Central アプリケーションにルートまたは中間の X.509 証明書が追加されます。 ルートまたは中間の証明書から派生したリーフ証明書を使用するデバイスをアプリケーションに接続できます。

ルート証明書とデバイス証明書を生成する

このセクションでは、X.509 証明書を使用して、IoT Central 登録グループの証明書から派生した証明書によりデバイスを接続します。

警告

この X.509 証明書の生成方法は、テストのみを目的としています。 運用環境では、証明書を生成するためのセキュリティで保護された正式なメカニズムを使用する必要があります。

  1. ダウンロードした Microsoft Azure IoT SDK for Node.js で、証明書生成スクリプトがある場所に移動します。 必要なパッケージをインストールします。

    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
    

    ヒント

    デバイス ID には、文字、数字、および - 文字を含めることができます。

これらのコマンドによって、以下のルート証明書とデバイス証明書が生成されます:

filename 目次
mytestrootcert_cert.pem X.509 ルート証明書の公開部分
mytestrootcert_key.pem X.509 ルート証明書の秘密キー
mytestrootcert_fullchain.pem X.509 ルート証明書のキーチェーン全体。
mytestrootcert.pfx ルート X509 証明書の PFX ファイル。
sampleDevice01_cert.pem X.509 デバイス証明書の公開部分
sampleDevice01_key.pem X.509 デバイス証明書の秘密キー
sampleDevice01_fullchain.pem X.509 デバイス証明書のキーチェーン全体。
sampleDevice01.pfx デバイス X509 証明書の PFX ファイル。

これらのファイルの場所を書き留めておいてください。 この情報は後で必要になります。

グループ登録を作成する

  1. IoT Central アプリケーションを開き、左側のペインで [アクセス許可] に移動し、[デバイス接続グループ] を選択します。

  2. [+ 新規] を選択し、構成証明書の種類として証明書 (X.509) を使用する、MyX509Group という名前の新しい登録グループを作成します。 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. プライマリ証明書の状態が [検証済み] になりました。

    Screenshot that shows a verified X509 certificate.

これで、このプライマリ ルート証明書から派生した X.509 証明書があるデバイスを接続できます。

登録グループを保存した後、ID スコープをメモしておきます。 この情報は後で必要になります。

サンプル デバイス コードを実行する

Windows を使用している場合、サンプルを機能させるには、X.509 証明書を Windows 証明書ストアに格納する必要があります。 Windows エクスプローラーで、以前生成した PFX ファイル mytestrootcert.pfx および sampleDevice01.pfx をダブルクリックします。 証明書のインポート ウィザードで、ストアの場所として [現在のユーザー] を選択し、パスワードとして1234を入力して、ウィザードに証明書ストアを自動的に選択させます。 ウィザードにより、現在のユーザーの個人用ストアに証明書がインポートされます。

X.509 証明書を使用するようにサンプル コードを変更するには:

  1. Visual Studio の IoTHubDeviceSamples ソリューションで、TemperatureController プロジェクトの Parameter.cs ファイルを開きます。

  2. 次の 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. Visual Studio の IoTHubDeviceSamples ソリューションで、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. 温度コントローラー デバイス サンプル用の pom.xml ファイルと src フォルダーが含まれる、azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample フォルダーに移動します。

  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 を次の import に置き換えます。

    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 を追加します。

    import java.nio.file.*;
    
  6. main メソッドでスローされる例外リストに SecurityProviderException を追加します。

    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. シェル環境で、次の 2 つの環境変数を追加します。 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. 次の 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. シェル環境で、次の 2 つの環境変数を追加します。 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. シェル環境で、次の 2 つの環境変数を追加します。 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 アプリケーションのデバイス ビューにテレメトリが表示されることを確認します。

Screenshot showing telemetry from a device that connected using 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 

ヒント

デバイス ID には、文字、数字、および - 文字を含めることができます。

これらのコマンドによって、以下のデバイス証明書が生成されます

filename 目次
mytestselfcertprimary_cert.pem プライマリ デバイス X.509 証明書の公開部分
mytestselfcertprimary_key.pem プライマリ デバイス X.509 証明書の秘密キー
mytestselfcertprimary_fullchain.pem プライマリ デバイス X.509 証明書のキーチェーン全体
mytestselfcertprimary.pfx プライマリ デバイス X.509 証明書の PFX ファイル。
mytestselfcertsecondary_cert.pem セカンダリ デバイス X.509 証明書の公開部分
mytestselfcertsecondary_key.pem セカンダリ デバイス X.509 証明書の秘密キー
mytestselfcertsecondary_fullchain.pem セカンダリ デバイス X.509 証明書のキーチェーン全体。
mytestselfcertsecondary.pfx セカンダリ デバイス X.509 証明書の PFX ファイル。

個別登録を作成する

  1. Azure IoT Central アプリケーションで、[デバイス] を選択し、サーモスタット デバイス テンプレートからデバイス ID として mytestselfcertprimary を使用する新しいデバイスを作成します。 ID スコープをメモしておきます。後で使用します。

  2. 作成したデバイスを開き、[接続] を選択します。

  3. 認証の種類として [個別の登録] を選択し、認証方法として [証明書 (X.509)] を選択します。

  4. 以前にプライマリ証明書として生成した mytestselfcertprimary_cert.pem ファイルをアップロードします。

  5. 以前にセカンダリ証明書として生成した mytestselfcertprimary_cert.pem ファイルをアップロードします。 その後、 [保存] を選びます。

  6. これで、デバイスに X.509 証明書を含む個別の登録が追加されました。

    Screenshot that shows how to connect a device using an X.509 individual enrollment.

サンプルの個別登録デバイス実行する

Windows を使用している場合、サンプルを機能させるには、X.509 証明書を Windows 証明書ストアに格納する必要があります。 Windows エクスプローラーで、以前生成した PFX ファイル mytestselfcertprimary.pfx および mytestselfcertsecondary.pfx をダブルクリックします。 証明書のインポート ウィザードで、ストアの場所として [現在のユーザー] を選択し、パスワードとして1234を入力して、ウィザードに証明書ストアを自動的に選択させます。 ウィザードにより、現在のユーザーの個人用ストアに証明書がインポートされます。

X.509 証明書を使用するようにサンプル コードを変更するには:

  1. Visual Studio の IoTHubDeviceSamples ソリューションで、TemperatureController プロジェクトの Parameter.cs ファイルを開きます。

  2. 次の 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. Visual Studio の IoTHubDeviceSamples ソリューションで、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. 温度コントローラー デバイス サンプル用の pom.xml ファイルと src フォルダーが含まれる、azure-iot-sdk-java/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample フォルダーに移動します。

  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 を次の import に置き換えます。

    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 を追加します。

    import java.nio.file.*;
    
  6. main メソッドでスローされる例外リストに SecurityProviderException を追加します。

    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. シェル環境で、次の 2 つの環境変数を追加します。 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. 次の 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. シェル環境で、次の 2 つの環境変数を追加します。 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. シェル環境で、次の 2 つの環境変数を追加します。 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 デバイスで、/etc/aziot/config.toml 構成ファイルの provisioning セクションを次のように編集します。

    # 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 証明書を使用して IoT Edge デバイスを Linux で大規模に作成およびプロビジョニングする方法に関する記事を参照してください。

ダウンストリーム デバイスを IoT Edge に接続する

IoT Edge では、ダウンストリーム デバイスと透過的ゲートウェイ役の IoT Edge デバイスの間の接続を保護するために X.509 証明書を使用します。 このシナリオの詳細については、「ダウンストリーム デバイスを 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. 証明書を更新する場合は、[プライマリの管理] または [Manage Secondary]\(セカンダリの管理\) を選択します。

  4. 登録グループで、ルート X.509 証明書を追加して検証します。

個々の登録とセキュリティ違反

セキュリティ違反に対する応答として証明書を展開する場合は、次のアプローチを使用して、現在の証明書をすぐに更新します。 これらの手順は、プライマリとセカンダリの証明書の両方が侵害されている場合に実行します。

  1. [デバイス] を選択し、デバイスを選択します。

  2. [接続] を選択し、接続方法として [個別の登録] を選択します。

  3. メカニズムとして [証明書 (X.509)] を選択します。

  4. 証明書を更新する場合は、フォルダー アイコンを選択し、登録エントリ用にアップロードする新しい証明書を選択します。 [保存] を選択します。

登録グループと証明書の期限切れ

証明書の有効期限切れに対応する場合は、次の方法を使用して、現在の証明書をすぐに更新します。

  1. 左側のペインで [アクセス許可] に移動し、[デバイス接続グループ] 選択します。

  2. [登録グループ] の一覧からグループ名を選択します。

  3. 証明書の更新については、[プライマリの管理] を選択します。

  4. 登録グループで、ルート X.509 証明書を追加して検証します。

  5. その後、セカンダリ証明書の有効期限が切れたら、ここに戻ってセカンダリ証明書を更新します。

個々の登録と証明書の期限切れ

証明書の期限切れを処理するために証明書を展開する場合、アプリケーションでプロビジョニングを試行するデバイスのダウンタイムが短縮されるように、セカンダリ証明書の構成を使用することをお勧めします。

セカンダリ証明書の期限切れが近づき、展開することが必要になったら、プライマリの構成を使用するよう切り替えることができます。 このようにプライマリとセカンダリの証明書を切り替えることで、アプリケーションでプロビジョニングを試行するデバイスのダウンタイムを短縮できます。

  1. [デバイス] を選択し、デバイスを選択します。

  2. [接続] を選択し、接続方法として [個別の登録] を選択します。

  3. メカニズムとして [証明書 (X.509)] を選択します。

  4. セカンダリ証明書を更新する場合は、フォルダー アイコンを選択し、登録エントリ用にアップロードする新しい証明書を選択します。 [保存] を選択します。

  5. その後、プライマリ証明書の有効期限が切れたら、ここに戻ってプライマリ証明書を更新します。