From 8d0a1fb31faf0b1992347ee9a78bb8b6aa6977ee Mon Sep 17 00:00:00 2001 From: "David R. Williamson" Date: Wed, 2 Dec 2020 17:46:08 -0800 Subject: [PATCH] Revamp DPS TPM sample --- .../Common/ProvisioningDeviceClientSample.cs | 79 ------------- .../ProvisioningDeviceClientSample.cs | 4 +- .../Samples/device/TpmSample/Parameters.cs | 52 +++++++++ .../Samples/device/TpmSample/Program.cs | 96 +++++++--------- .../ProvisioningDeviceClientSample.cs | 107 ++++++++++++++++++ .../Samples/device/TpmSample/TpmSample.csproj | 10 +- .../Samples/device/TpmSample/readme.md | 18 ++- .../ProvisioningDeviceClientSample.cs | 2 +- .../Samples/device/X509Sample/readme.md | 2 +- provisioning/Samples/device/readme.md | 54 ++++----- .../SecurityProviderTpmSimulator/.gitignore | 1 + .../SecurityProviderTpmSimulator.cs | 22 ++-- 12 files changed, 257 insertions(+), 190 deletions(-) delete mode 100644 provisioning/Samples/device/Common/ProvisioningDeviceClientSample.cs create mode 100644 provisioning/Samples/device/TpmSample/Parameters.cs create mode 100644 provisioning/Samples/device/TpmSample/ProvisioningDeviceClientSample.cs create mode 100644 security/Samples/SecurityProviderTpmSimulator/.gitignore diff --git a/provisioning/Samples/device/Common/ProvisioningDeviceClientSample.cs b/provisioning/Samples/device/Common/ProvisioningDeviceClientSample.cs deleted file mode 100644 index 9548990b..00000000 --- a/provisioning/Samples/device/Common/ProvisioningDeviceClientSample.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Azure.Devices.Client; -using Microsoft.Azure.Devices.Provisioning.Client; -using Microsoft.Azure.Devices.Shared; -using System; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace Microsoft.Azure.Devices.Provisioning.Client.Samples -{ - public class ProvisioningDeviceClientSample - { - ProvisioningDeviceClient _provClient; - SecurityProvider _security; - - public ProvisioningDeviceClientSample(ProvisioningDeviceClient provisioningDeviceClient, SecurityProvider security) - { - _provClient = provisioningDeviceClient; - _security = security; - } - - public async Task RunSampleAsync() - { - Console.WriteLine($"RegistrationID = {_security.GetRegistrationID()}"); - VerifyRegistrationIdFormat(_security.GetRegistrationID()); - - Console.Write("ProvisioningClient RegisterAsync . . . "); - DeviceRegistrationResult result = await _provClient.RegisterAsync().ConfigureAwait(false); - - Console.WriteLine($"{result.Status}"); - Console.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}"); - - if (result.Status != ProvisioningRegistrationStatusType.Assigned) return; - - IAuthenticationMethod auth; - if (_security is SecurityProviderTpm) - { - Console.WriteLine("Creating TPM DeviceClient authentication."); - auth = new DeviceAuthenticationWithTpm(result.DeviceId, _security as SecurityProviderTpm); - } - else if (_security is SecurityProviderX509) - { - Console.WriteLine("Creating X509 DeviceClient authentication."); - auth = new DeviceAuthenticationWithX509Certificate(result.DeviceId, (_security as SecurityProviderX509).GetAuthenticationCertificate()); - } - else if (_security is SecurityProviderSymmetricKey) - { - Console.WriteLine("Creating Symmetric Key DeviceClient authenication"); - auth = new DeviceAuthenticationWithRegistrySymmetricKey(result.DeviceId, (_security as SecurityProviderSymmetricKey).GetPrimaryKey()); - } - else - { - throw new NotSupportedException("Unknown authentication type."); - } - - using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, TransportType.Amqp)) - { - Console.WriteLine("DeviceClient OpenAsync."); - await iotClient.OpenAsync().ConfigureAwait(false); - Console.WriteLine("DeviceClient SendEventAsync."); - await iotClient.SendEventAsync(new Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false); - Console.WriteLine("DeviceClient CloseAsync."); - await iotClient.CloseAsync().ConfigureAwait(false); - } - } - - private void VerifyRegistrationIdFormat(string v) - { - var r = new Regex("^[a-z0-9-]*$"); - if (!r.IsMatch(v)) - { - throw new FormatException("Invalid registrationId: The registration ID is alphanumeric, lowercase, and may contain hyphens"); - } - } - } -} diff --git a/provisioning/Samples/device/SymmetricKeySample/ProvisioningDeviceClientSample.cs b/provisioning/Samples/device/SymmetricKeySample/ProvisioningDeviceClientSample.cs index c6b0a6a2..f4580c9f 100644 --- a/provisioning/Samples/device/SymmetricKeySample/ProvisioningDeviceClientSample.cs +++ b/provisioning/Samples/device/SymmetricKeySample/ProvisioningDeviceClientSample.cs @@ -53,9 +53,9 @@ public async Task RunSampleAsync() security, transportHandler); - Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}"); + Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}."); - Console.WriteLine("Registering with the device provisioning service... "); + Console.WriteLine("Registering with the device provisioning service..."); DeviceRegistrationResult result = await provClient.RegisterAsync(); Console.WriteLine($"Registration status: {result.Status}."); diff --git a/provisioning/Samples/device/TpmSample/Parameters.cs b/provisioning/Samples/device/TpmSample/Parameters.cs new file mode 100644 index 00000000..b6b43e59 --- /dev/null +++ b/provisioning/Samples/device/TpmSample/Parameters.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using CommandLine; +using Microsoft.Azure.Devices.Client; + +namespace Microsoft.Azure.Devices.Provisioning.Client.Samples +{ + /// + /// Parameters for the application + /// + internal class Parameters + { + [Option( + 'e', + "GetTpmEndorsementKey", + HelpText = "Gets the TPM endorsement key. Use this option by itself to get the EK needed to create a DPS individual enrollment.")] + public bool GetTpmEndorsementKey { get; set; } + + [Option( + 'u', + "UseTpmSimulator", + HelpText = "Runs the TPM simulator - useful when the local device does not have a TPM chip.")] + public bool UseTpmSimulator { get; set; } + + [Option( + 's', + "IdScope", + HelpText = "The Id Scope of the DPS instance. For normal runs, this is required.")] + public string IdScope { get; set; } + + [Option( + 'r', + "RegistrationId", + HelpText = "The registration Id from the individual enrollment. For normal runs, this is required.")] + public string RegistrationId { get; set; } + + [Option( + 'g', + "GlobalDeviceEndpoint", + Default = "global.azure-devices-provisioning.net", + HelpText = "The global endpoint for devices to connect to.")] + public string GlobalDeviceEndpoint { get; set; } + + [Option( + 't', + "TransportType", + Default = TransportType.Amqp, + HelpText = "The transport to use to communicate with the device provisioning instance. Possible values include Amqp, Amqp_WebSocket_Only, Amqp_Tcp_only, and Http1.")] + public TransportType TransportType { get; set; } + } +} diff --git a/provisioning/Samples/device/TpmSample/Program.cs b/provisioning/Samples/device/TpmSample/Program.cs index 3a6936d2..17573e9c 100644 --- a/provisioning/Samples/device/TpmSample/Program.cs +++ b/provisioning/Samples/device/TpmSample/Program.cs @@ -1,76 +1,64 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Azure.Devices.Provisioning.Client; -using Microsoft.Azure.Devices.Provisioning.Client.Transport; +using CommandLine; +using Microsoft.Azure.Devices.Provisioning.Security; using Microsoft.Azure.Devices.Provisioning.Security.Samples; -using Microsoft.Azure.Devices.Shared; using System; +using System.Threading.Tasks; namespace Microsoft.Azure.Devices.Provisioning.Client.Samples { + /// + /// A sample to illustrate connecting a device to hub using the device provisioning service and a certificate. + /// public static class Program { - // The Provisioning Hub IDScope. - - // For this sample either: - // - pass this value as a command-prompt argument - // - set the DPS_IDSCOPE environment variable - // - create a launchSettings.json (see launchSettings.json.template) containing the variable - private static string s_idScope = Environment.GetEnvironmentVariable("DPS_IDSCOPE"); - - private const string RegistrationId = "testtpmregistration1"; - private const string GlobalDeviceEndpoint = "global.azure-devices-provisioning.net"; - - public static int Main(string[] args) + public static async Task Main(string[] args) { - if (string.IsNullOrWhiteSpace(s_idScope) && (args.Length > 0)) - { - s_idScope = args[0]; - } - - if (string.IsNullOrWhiteSpace(s_idScope)) - { - Console.WriteLine("ProvisioningDeviceClientTpm "); - return 1; - } - - // Remove if a real TPM is being used. - Console.WriteLine("Starting TPM simulator."); - SecurityProviderTpmSimulator.StartSimulatorProcess(); - - // Replace the following type with SecurityProviderTpmHsm() to use a real TPM2.0 device. - using (var security = new SecurityProviderTpmSimulator(RegistrationId)) + // Parse application parameters + Parameters parameters = null; + ParserResult result = Parser.Default.ParseArguments(args) + .WithParsed(parsedParams => + { + parameters = parsedParams; + }) + .WithNotParsed(errors => + { + Environment.Exit(1); + }); - // Select one of the available transports: - // To optimize for size, reference only the protocols used by your application. - using (var transport = new ProvisioningTransportHandlerHttp()) - // using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly)) - // using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.WebSocketOnly)) + // This sample provides a way to get the endorsement key (EK) required in creation of the individual enrollment + if (parameters.GetTpmEndorsementKey) { - // Note that the TPM simulator will create an NVChip file containing the simulated TPM state. - Console.WriteLine("Extracting endorsement key."); - string base64EK = Convert.ToBase64String(security.GetEndorsementKey()); + if (parameters.UseTpmSimulator) + { + Console.WriteLine("Starting TPM simulator..."); + SecurityProviderTpmSimulator.StartSimulatorProcess(); + } - Console.WriteLine( - "In your Azure Device Provisioning Service please go to 'Manage enrollments' and select " + - "'Individual Enrollments'. Select 'Add' then fill in the following:"); + using var security = new SecurityProviderTpmHsm(null); + Console.WriteLine($"Your EK is {Convert.ToBase64String(security.GetEndorsementKey())}"); - Console.WriteLine("\tMechanism: TPM"); - Console.WriteLine($"\tRegistration ID: {RegistrationId}"); - Console.WriteLine($"\tEndorsement key: {base64EK}"); - Console.WriteLine("\tDevice ID: iothubtpmdevice1 (or any other valid DeviceID)"); - Console.WriteLine(); - Console.WriteLine("Press ENTER when ready."); - Console.ReadLine(); + if (parameters.UseTpmSimulator) + { + SecurityProviderTpmSimulator.StopSimulatorProcess(); + } - ProvisioningDeviceClient provClient = - ProvisioningDeviceClient.Create(GlobalDeviceEndpoint, s_idScope, security, transport); + return 0; + } - var sample = new ProvisioningDeviceClientSample(provClient, security); - sample.RunSampleAsync().GetAwaiter().GetResult(); + // For a normal run of this sample, IdScope and RegistrationId are required + if (string.IsNullOrWhiteSpace(parameters.IdScope) + || string.IsNullOrWhiteSpace(parameters.RegistrationId)) + { + Console.WriteLine(CommandLine.Text.HelpText.AutoBuild(result, null, null)); + Environment.Exit(1); } + var sample = new ProvisioningDeviceClientSample(parameters); + await sample.RunSampleAsync(); + return 0; } } diff --git a/provisioning/Samples/device/TpmSample/ProvisioningDeviceClientSample.cs b/provisioning/Samples/device/TpmSample/ProvisioningDeviceClientSample.cs new file mode 100644 index 00000000..5036ff34 --- /dev/null +++ b/provisioning/Samples/device/TpmSample/ProvisioningDeviceClientSample.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Azure.Devices.Client; +using Microsoft.Azure.Devices.Provisioning.Client.Transport; +using Microsoft.Azure.Devices.Provisioning.Security; +using Microsoft.Azure.Devices.Provisioning.Security.Samples; +using Microsoft.Azure.Devices.Shared; +using System; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Devices.Provisioning.Client.Samples +{ + /// + /// Demonstrates how to register a device with the device provisioning service using a certificate, and then + /// use the registration information to authenticate to IoT Hub. + /// + internal class ProvisioningDeviceClientSample + { + private readonly Parameters _parameters; + + public ProvisioningDeviceClientSample(Parameters parameters) + { + _parameters = parameters; + } + + public async Task RunSampleAsync() + { + SecurityProviderTpm security = null; + + try + { + if (_parameters.UseTpmSimulator) + { + Console.WriteLine("Starting TPM simulator..."); + SecurityProviderTpmSimulator.StartSimulatorProcess(); + security = new SecurityProviderTpmSimulator(_parameters.RegistrationId); + } + else + { + Console.WriteLine("Initializing security using the local TPM..."); + security = new SecurityProviderTpmHsm(_parameters.RegistrationId); + } + + Console.WriteLine($"Initializing the device provisioning client..."); + + using var transport = GetTransportHandler(); + ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + _parameters.GlobalDeviceEndpoint, + _parameters.IdScope, + security, + transport); + + Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}."); + + Console.WriteLine("Registering with the device provisioning service... "); + DeviceRegistrationResult result = await provClient.RegisterAsync(); + + Console.WriteLine($"Registration status: {result.Status}."); + if (result.Status != ProvisioningRegistrationStatusType.Assigned) + { + Console.WriteLine($"Registration status did not assign a hub, so exiting this sample."); + return; + } + + Console.WriteLine($"Device {result.DeviceId} registered to {result.AssignedHub}."); + + Console.WriteLine("Creating TPM authentication for IoT Hub..."); + IAuthenticationMethod auth = new DeviceAuthenticationWithTpm(result.DeviceId, security); + + Console.WriteLine($"Testing the provisioned device with IoT Hub..."); + using DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, _parameters.TransportType); + + Console.WriteLine("Sending a telemetry message..."); + using var message = new Message(Encoding.UTF8.GetBytes("TestMessage")); + await iotClient.SendEventAsync(message); + } + finally + { + if (_parameters.UseTpmSimulator) + { + SecurityProviderTpmSimulator.StopSimulatorProcess(); + } + + security?.Dispose(); + } + + Console.WriteLine("Finished."); + } + + private ProvisioningTransportHandler GetTransportHandler() + { + return _parameters.TransportType switch + { + TransportType.Amqp => new ProvisioningTransportHandlerAmqp(), + TransportType.Amqp_Tcp_Only => new ProvisioningTransportHandlerAmqp(TransportFallbackType.WebSocketOnly), + TransportType.Amqp_WebSocket_Only => new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly), + TransportType.Http1 => new ProvisioningTransportHandlerHttp(), + TransportType.Mqtt => throw new NotSupportedException("MQTT is not supported for TPM"), + TransportType.Mqtt_Tcp_Only => throw new NotSupportedException("MQTT is not supported for TPM"), + TransportType.Mqtt_WebSocket_Only => throw new NotSupportedException("MQTT is not supported for TPM"), + _ => throw new NotSupportedException($"Unsupported transport type {_parameters.TransportType}"), + }; + } + } +} diff --git a/provisioning/Samples/device/TpmSample/TpmSample.csproj b/provisioning/Samples/device/TpmSample/TpmSample.csproj index bb1ee635..bc1776c6 100644 --- a/provisioning/Samples/device/TpmSample/TpmSample.csproj +++ b/provisioning/Samples/device/TpmSample/TpmSample.csproj @@ -8,17 +8,15 @@ - - - - + - + - + + diff --git a/provisioning/Samples/device/TpmSample/readme.md b/provisioning/Samples/device/TpmSample/readme.md index 0b265ea7..b88c2bca 100644 --- a/provisioning/Samples/device/TpmSample/readme.md +++ b/provisioning/Samples/device/TpmSample/readme.md @@ -6,13 +6,19 @@ This is a quick tutorial with the steps to register a device in the Microsoft Az ## How to run the sample -Ensure that all prerequisite steps presented in [samples](../) have been performed. -To run the sample, in a developer command prompt enter: +1. Ensure that all prerequisite steps presented in [samples](../) have been performed. +1. The sample must be run in administrative mode, so open VS as an admin, or open a console window as an admin. +1. You'll need the endorsement key (EK) of your TPM device to create an individual enrollment. This sample has a parameter `--GetTpmEndorsementKey` that can be used to get it and print it to the console. +1. If using a console, enter: `dotnet run -s -r `. +1. If using VS, edit project properties | debug | application arguments and add the parameters: `-s -r ` -`dotnet run ` - -replacing `IDScope` with the value found within the Device Provisioning Service Overview tab. E.g. `dotnet run 0ne1234ABCD` +> Replace `IdScope` with the value found within the Device Provisioning Service Overview tab, and `RegistrationId` with the individual enrollment registration Id. +> To see a full list of parameters, run `dotnet run -?`. Continue by following the instructions presented by the sample. -The sample is currently using `SecurityProviderTpmSimulator` which is not supported on Linux. To run against a real TPM2.0 device, replace this with `SecurityProviderTpm`. +## Notes + +For convenience, this sample uses a TPM simulator (SecurityProviderTpmSimulator) which is not supported on Linux. + +To run against a real TPM2.0 device, replace this with `SecurityProviderTpmHsm`. diff --git a/provisioning/Samples/device/X509Sample/ProvisioningDeviceClientSample.cs b/provisioning/Samples/device/X509Sample/ProvisioningDeviceClientSample.cs index cd25d85a..4cee40c7 100644 --- a/provisioning/Samples/device/X509Sample/ProvisioningDeviceClientSample.cs +++ b/provisioning/Samples/device/X509Sample/ProvisioningDeviceClientSample.cs @@ -40,7 +40,7 @@ public async Task RunSampleAsync() security, transport); - Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}"); + Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}."); Console.WriteLine("Registering with the device provisioning service... "); DeviceRegistrationResult result = await provClient.RegisterAsync(); diff --git a/provisioning/Samples/device/X509Sample/readme.md b/provisioning/Samples/device/X509Sample/readme.md index 7ff42cfd..d3b18a15 100644 --- a/provisioning/Samples/device/X509Sample/readme.md +++ b/provisioning/Samples/device/X509Sample/readme.md @@ -25,7 +25,7 @@ Certificate: Select the public key 'certificate.cer' file. To run the sample, in a developer command prompt enter: `dotnet run -s ` -> Replace `IdScope` with the value found within the Device Provisioning Service Overview tab. E.g. `dotnet run 0ne1234ABCD` +> Replace `IdScope` with the value found within the Device Provisioning Service Overview tab. > To see a full list of parameters, run `dotnet run -?`. Continue by following the instructions presented in the sample console window. diff --git a/provisioning/Samples/device/readme.md b/provisioning/Samples/device/readme.md index d2b134df..351ca859 100644 --- a/provisioning/Samples/device/readme.md +++ b/provisioning/Samples/device/readme.md @@ -6,33 +6,26 @@ This folder contains samples demonstrating the steps required to dynamically ass To ensure that only authorized devices can be provisioned, two device attestation mechanisms are supported by the service: one based on X.509 certificates and another based on Trusted Platform Module (TPM) devices. In both cases, public/private key authentication will be performed during provisioning. -_Preview only:_ The SDK currently supports two communication protocols: HTTP and AMQP over TLS. - ## Device provisioning in a nutshell Provisioning is achieved by using a single call to the `ProvisioningDeviceClient.RegisterAsync()` API specifying the `GlobalDeviceEndpoint`, the IDScope (unique for each Provisioning Service deployment), a `SecurityProvider` and a `ProvisioningTransportHandler`: ```C# - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(globalDeviceEndpoint, s_idScope, security, transport); - DeviceRegistrationResult result = await provClient.RegisterAsync(); - if (result.Status != ProvisioningRegistrationStatusType.Assigned) - { - Console.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}"); - } +ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(globalDeviceEndpoint, s_idScope, security, transport); +DeviceRegistrationResult result = await provClient.RegisterAsync(); +if (result.Status != ProvisioningRegistrationStatusType.Assigned) +{ + Console.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceId: {result.DeviceId}"); +} ``` -To change the transport between AMQP, HTTP and MQTT change the following line: -```C# - using (var transport = new ProvisioningTransportHandlerHttp()) - using (var transport = new ProvisioningTransportHandlerAmqp()) - using (var transport = new ProvisioningTransportHandlerMqtt()) -``` +To change the transport between AMQP, HTTP and MQTT, check the sample parameters (`-h`). ### Provisioning devices using X.509 certificate-based attestation Devices must have access to a single certificate with a private key. The private key can be hidden from the application using PKCS #11 Hardware Security Modules. -When Group Enrollment is used, both the _RegistrationID_ as well as the _DeviceID_ will be equal to the Common Name portion of the certificate Subject. (e.g. If the subject is `CN=mydevice O=Contoso C=US`, the RegistrationID and DeviceID will be `mydevice`.) The name must respect the [DeviceID naming constraints](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-identity-registry). +When Group Enrollment is used, both the _RegistrationID_ as well as the _DeviceId_ will be equal to the Common Name portion of the certificate Subject. (e.g. If the subject is `CN=mydevice O=Contoso C=US`, the RegistrationID and DeviceId will be `mydevice`.) The name must respect the [DeviceId naming constraints](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-identity-registry). X.509 attestation comes in two flavors: @@ -51,29 +44,24 @@ _Note:_ the device certificate can also be a signing certificate of the actual c An example of specifying the authentication X509Certificate using a PKCS12 PFX password-protected file: ```C# - using (var certificate = new X509Certificate2(s_certificateFileName, certificatePassword)) - using (var security = new SecurityProviderX509Certificate(certificate)) - { - // ... (see sample for details) - } +using var certificate = new X509Certificate2(s_certificateFileName, certificatePassword); +using var security = new SecurityProviderX509Certificate(certificate); +// ... (see sample for details) ``` -The SDK provides an extension model [SecurityProviderX509](https://github.com/Azure/azure-iot-sdk-csharp/blob/master/shared/src/SecurityProviderX509.cs) that allows hardware vendors to implement custom Hardware Security Modules that store the device certificates. On Windows, PKCS11 HSM devices are supported through the [Certificate Store](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/certificate-stores). +The SDK provides an extension model [SecurityProviderX509](https://github.com/Azure/azure-iot-sdk-csharp/blob/master/shared/src/SecurityProviderX509.cs) that allows hardware vendors to implement custom Hardware Security Modules that store the device certificates. On Windows, PKCS11 HSM devices are supported through the [Certificate Store](https://docs.microsoft.com/windows-hardware/drivers/install/certificate-stores). An example of implementation for this extension module is the [SecurityProviderX509Certificate](https://github.com/Azure/azure-iot-sdk-csharp/blob/master/shared/src/SecurityProviderX509Certificate.cs) class. -### Provisioning devices using TPM based attestation +### Provisioning devices using TPM-based attestation In the case of TPM attestation, a RegistrationID must be supplied by the application. -The TPM attestation supports only Individual Enrollments. The [Endorsement Key](https://technet.microsoft.com/en-us/library/cc770443(v=ws.11).aspx) (EK) must be supplied to the service. During provisioning, the [Storage Root Key](https://technet.microsoft.com/en-us/library/cc753560(v=ws.11).aspx) (SRK) will also be used to ensure that TPM ownership changes result in devices provisioned to the correct owner. - +The TPM attestation supports only Individual Enrollments. The [Endorsement Key](https://technet.microsoft.com/library/cc770443(v=ws.11).aspx) (EK) must be supplied to the service. During provisioning, the [Storage Root Key](https://technet.microsoft.com/library/cc753560(v=ws.11).aspx) (SRK) will also be used to ensure that TPM ownership changes result in devices provisioned to the correct owner. ```C# - using (var security = new SecurityProviderTpmSimulator(RegistrationId)) - { - // ... (see sample for details) - } +using var security = new SecurityProviderTpmSimulator(RegistrationId); +// ... (see sample for details) ``` The SDK provides an extension model [SecurityProviderTpm](https://github.com/Azure/azure-iot-sdk-csharp/blob/master/shared/src/SecurityProviderTpm.cs) that allows hardware vendors to implement custom TPM v2.0 Hardware Security Modules. @@ -88,13 +76,11 @@ The samples use a TPMv2.0 simulator that uses a loopback TCP connection for comm ### How to run the samples 1. Prepare your development environment. Follow the instructions at ./doc/devbox_setup.md +1. Setup your IoT Hub Device Provisioning Service and associated IoT Hub. Follow the instructions at . +1. Continue following specific instructions in each of the sample folders. -2. Setup your IoT Hub Device Provisioning Service and associated IoT Hub. Follow the instructions at https://docs.microsoft.com/en-us/azure/iot-dps/quick-setup-auto-provision - -4. Continue following specific instructions in each of the sample folders. - -### Read More +### Read more -- [Azure IoT Hub Device Provisioning Service (preview) Documentation](https://docs.microsoft.com/en-us/azure/iot-dps/) +- [Azure IoT Hub Device Provisioning Service (preview) Documentation](https://docs.microsoft.com/azure/iot-dps/) - [How to use the Azure IoT SDKs for .NET](https://github.com/azure/azure-iot-sdk-csharp#how-to-use-the-azure-iot-sdks-for-net) diff --git a/security/Samples/SecurityProviderTpmSimulator/.gitignore b/security/Samples/SecurityProviderTpmSimulator/.gitignore new file mode 100644 index 00000000..e94a1012 --- /dev/null +++ b/security/Samples/SecurityProviderTpmSimulator/.gitignore @@ -0,0 +1 @@ +NVChip \ No newline at end of file diff --git a/security/Samples/SecurityProviderTpmSimulator/SecurityProviderTpmSimulator.cs b/security/Samples/SecurityProviderTpmSimulator/SecurityProviderTpmSimulator.cs index 0015fb54..12618ee2 100644 --- a/security/Samples/SecurityProviderTpmSimulator/SecurityProviderTpmSimulator.cs +++ b/security/Samples/SecurityProviderTpmSimulator/SecurityProviderTpmSimulator.cs @@ -4,7 +4,6 @@ using Microsoft.Azure.Devices.Shared; using System; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -28,11 +27,14 @@ public class SecurityProviderTpmSimulator : SecurityProviderTpm private static string s_simulatorExeName; + private static int s_simulatorProcessId; + private TcpTpmDevice _tcpTpmDevice; private Tpm2 _tpm2; private SecurityProviderTpmHsm _innerClient; - public SecurityProviderTpmSimulator(string registrationId) : base(registrationId) + public SecurityProviderTpmSimulator(string registrationId) + : base(registrationId) { _tcpTpmDevice = new TcpTpmDevice(SimulatorAddress, SimulatorPort); _tcpTpmDevice.Connect(); @@ -65,17 +67,22 @@ public override byte[] Sign(byte[] data) } - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Sample code resiliency")] public static void StopSimulatorProcess() { - foreach (var process in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(s_simulatorExeName))) + if (s_simulatorProcessId != 0) { try { - process?.Kill(); + Process process = Process.GetProcessById(s_simulatorProcessId); + process.Kill(); + } + catch (ArgumentException) + { + // Process not found } - catch (Exception) + finally { + s_simulatorProcessId = 0; } } } @@ -116,7 +123,7 @@ public static void StartSimulatorProcess() if (files.Length == 0) { - throw new InvalidOperationException($"TPM Simulator not found : {s_simulatorExeName}"); + throw new InvalidOperationException($"TPM Simulator not found: {s_simulatorExeName}"); } using var simulatorProcess = new Process @@ -130,6 +137,7 @@ public static void StartSimulatorProcess() }; simulatorProcess.Start(); + s_simulatorProcessId = simulatorProcess.Id; } protected override void Dispose(bool disposing)