Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Commit 5302f5c

Browse files
author
David R. Williamson
authored
Merge pull request #164 from Azure-Samples/drwill/dpsX509
Update device DPS x509 sample
2 parents 0bc82f8 + 5759f55 commit 5302f5c

File tree

12 files changed

+303
-157
lines changed

12 files changed

+303
-157
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ ClientBin/
198198
*.dbproj.schemaview
199199
*.jfm
200200
*.pfx
201+
*.cer
201202
*.publishsettings
202203
orleans.codegen.cs
203204

provisioning/Samples/device/SymmetricKeySample/EnrollmentType.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples
25
{
36
/// <summary>
47
/// The type of enrollment for a device in the provisioning service.

provisioning/Samples/device/SymmetricKeySample/Parameters.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using CommandLine;
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using CommandLine;
25
using Microsoft.Azure.Devices.Client;
36

47
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples

provisioning/Samples/device/SymmetricKeySample/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace SymmetricKeySample
1010
{
1111
/// <summary>
12-
/// A sample to illustrate connecting a device to hub using the device provisioning service.
12+
/// A sample to illustrate connecting a device to hub using the device provisioning service and a symmetric key.
1313
/// </summary>
1414
internal class Program
1515
{

provisioning/Samples/device/SymmetricKeySample/ProvisioningDeviceClientSample.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples
1313
{
1414
/// <summary>
15-
/// Demonstrates how to register a device with the device provisioning service, and then
15+
/// Demonstrates how to register a device with the device provisioning service using a symmetric key, and then
1616
/// use the registration information to authenticate to IoT Hub.
1717
/// </summary>
1818
internal class ProvisioningDeviceClientSample
@@ -45,16 +45,18 @@ public async Task RunSampleAsync()
4545
_parameters.PrimaryKey,
4646
null);
4747

48+
using var transportHandler = GetTransportHandler();
49+
4850
ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(
4951
_parameters.GlobalDeviceEndpoint,
5052
_parameters.IdScope,
5153
security,
52-
GetTransportHandler());
54+
transportHandler);
5355

5456
Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}");
5557

5658
Console.WriteLine("Registering with the device provisioning service... ");
57-
DeviceRegistrationResult result = await provClient.RegisterAsync().ConfigureAwait(false);
59+
DeviceRegistrationResult result = await provClient.RegisterAsync();
5860

5961
Console.WriteLine($"Registration status: {result.Status}.");
6062
if (result.Status != ProvisioningRegistrationStatusType.Assigned)

provisioning/Samples/device/SymmetricKeySample/SymmetricKeySample.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<ItemGroup>
1010
<PackageReference Include="CommandLineParser" Version="2.8.0" />
1111
<PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.33.1" />
12+
<PackageReference Include="Microsoft.Azure.Devices.Provisioning.Client" Version="1.16.2" />
13+
14+
<!-- Note: Applications should not need to import all 3 protocols. This was done to simplify protocol selection within the sample.-->
1215
<PackageReference Include="Microsoft.Azure.Devices.Provisioning.Transport.Amqp" Version="1.13.3" />
1316
<PackageReference Include="Microsoft.Azure.Devices.Provisioning.Transport.Http" Version="1.12.2" />
1417
<PackageReference Include="Microsoft.Azure.Devices.Provisioning.Transport.Mqtt" Version="1.13.2" />

provisioning/Samples/device/X509Sample/GenerateTestCertificate.ps1

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Self-signed device certificate generator.
99
.DESCRIPTION
1010
Generates an X509 test certificate with the specified Common Name (CN).
1111
12-
.\GenerateTestCertificate.ps1 <DeviceID>
12+
.\GenerateTestCertificate.ps1 <DeviceId>
1313
1414
.EXAMPLE
1515
.\GenerateTestCertificate.ps1 testdevice1
@@ -23,11 +23,20 @@ https://github.com/azure/azure-iot-sdk-csharp
2323
#>
2424

2525
Param(
26-
$deviceName = "iothubx509device1",
27-
$certificateValidityInYears = 1
26+
$deviceName = "iothubx509device1",
27+
$certificateValidityInYears = 1
2828
)
2929

30-
$cert = New-SelfSignedCertificate -Type Custom -Subject "CN=$deviceName, O=TEST, C=US" -KeySpec Signature -KeyExportPolicy Exportable -HashAlgorithm sha256 -KeyLength 2048 -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2") -CertStoreLocation "Cert:\CurrentUser\My" -NotAfter (Get-Date).AddYears($certificateValidityInYears)
30+
$cert = New-SelfSignedCertificate `
31+
-Type Custom `
32+
-Subject "CN=$deviceName, O=TEST, C=US" `
33+
-KeySpec Signature `
34+
-KeyExportPolicy Exportable `
35+
-HashAlgorithm sha256 `
36+
-KeyLength 2048 `
37+
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2") `
38+
-CertStoreLocation "Cert:\CurrentUser\My" `
39+
-NotAfter (Get-Date).AddYears($certificateValidityInYears)
3140

3241
Write-Host "Generated the certificate:"
3342
Write-Host $cert
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using CommandLine;
5+
using Microsoft.Azure.Devices.Client;
6+
using System;
7+
using System.IO;
8+
using System.Reflection;
9+
10+
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples
11+
{
12+
/// <summary>
13+
/// Parameters for the application
14+
/// </summary>
15+
internal class Parameters
16+
{
17+
[Option(
18+
's',
19+
"IdScope",
20+
Required = true,
21+
HelpText = "The Id Scope of the DPS instance")]
22+
public string IdScope { get; set; }
23+
24+
[Option(
25+
'c',
26+
"CertificateName",
27+
Default = "certificate.pfx",
28+
HelpText = "The PFX certificate to load for device provisioning authentication.")]
29+
public string CertificateName { get; set; }
30+
31+
[Option(
32+
'p',
33+
"CertificatePassword",
34+
HelpText = "The password of the PFX certificate file. If not specified, the program will prompt at run time.")]
35+
public string CertificatePassword { get; set; }
36+
37+
[Option(
38+
'g',
39+
"GlobalDeviceEndpoint",
40+
Default = "global.azure-devices-provisioning.net",
41+
HelpText = "The global endpoint for devices to connect to.")]
42+
public string GlobalDeviceEndpoint { get; set; }
43+
44+
[Option(
45+
't',
46+
"TransportType",
47+
Default = TransportType.Mqtt,
48+
HelpText = "The transport to use to communicate with the device provisioning instance. Possible values include Mqtt, Mqtt_WebSocket_Only, Mqtt_Tcp_Only, Amqp, Amqp_WebSocket_Only, Amqp_Tcp_only, and Http1.")]
49+
public TransportType TransportType { get; set; }
50+
51+
public string GetCertificatePath()
52+
{
53+
if (string.IsNullOrWhiteSpace(CertificateName))
54+
{
55+
throw new InvalidOperationException("The certificate name has not been set.");
56+
}
57+
58+
string codeBase = Assembly.GetExecutingAssembly().Location;
59+
string workingDirectory = Path.GetDirectoryName(codeBase);
60+
61+
// Ascend the directory looking for one that has a certficate with the specified name,
62+
// because the sample exe is likely in a build output folder ~3 levels below in
63+
// the project folder.
64+
while (!string.IsNullOrWhiteSpace(workingDirectory))
65+
{
66+
string certificatePath = Path.Combine(workingDirectory, CertificateName);
67+
if (File.Exists(certificatePath))
68+
{
69+
return certificatePath;
70+
}
71+
72+
workingDirectory = Directory.GetParent(workingDirectory)?.FullName;
73+
}
74+
75+
// Once we get to the root, the call to parent will return null
76+
// so that is our failure condition.
77+
throw new InvalidOperationException($"Could not find the certificate file {CertificateName} in the sample execution folder or any parent folder.");
78+
}
79+
}
80+
}
Lines changed: 18 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,35 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using Microsoft.Azure.Devices.Provisioning.Client;
5-
using Microsoft.Azure.Devices.Provisioning.Client.Transport;
6-
using Microsoft.Azure.Devices.Shared;
4+
using CommandLine;
75
using System;
8-
using System.IO;
9-
using System.Security.Cryptography.X509Certificates;
10-
using System.Text;
6+
using System.Threading.Tasks;
117

128
namespace Microsoft.Azure.Devices.Provisioning.Client.Samples
139
{
10+
/// <summary>
11+
/// A sample to illustrate connecting a device to hub using the device provisioning service and a certificate.
12+
/// </summary>
1413
public static class Program
1514
{
16-
// The Provisioning Hub IDScope.
17-
18-
// For this sample either:
19-
// - pass this value as a command-prompt argument
20-
// - set the DPS_IDSCOPE environment variable
21-
// - create a launchSettings.json (see launchSettings.json.template) containing the variable
22-
private static string s_idScope = Environment.GetEnvironmentVariable("DPS_IDSCOPE");
23-
24-
// In your Device Provisioning Service please go to "Manage enrollments" and select "Individual Enrollments".
25-
// Select "Add individual enrollment" then fill in the following:
26-
// Mechanism: X.509
27-
// Certificate:
28-
// You can generate a self-signed certificate by running the GenerateTestCertificate.ps1 powershell script.
29-
// Select the public key 'certificate.cer' file. ('certificate.pfx' contains the private key and is password protected.)
30-
// For production code, it is advised that you install the certificate in the CurrentUser (My) store.
31-
// DeviceID: iothubx509device1
32-
33-
// X.509 certificates may also be used for enrollment groups.
34-
// In your Device Provisioning Service please go to "Manage enrollments" and select "Enrollment Groups".
35-
// Select "Add enrollment group" then fill in the following:
36-
// Group name: <your group name>
37-
// Attestation Type: Certificate
38-
// Certificate Type:
39-
// choose CA certificate then link primary and secondary certificates
40-
// OR choose Intermediate certificate and upload primary and secondary certificate files
41-
// You may also change other enrollemtn group parameters according to your needs
42-
43-
private const string GlobalDeviceEndpoint = "global.azure-devices-provisioning.net";
44-
private static string s_certificateFileName = "certificate.pfx";
45-
46-
public static int Main(string[] args)
15+
public static async Task<int> Main(string[] args)
4716
{
48-
if (string.IsNullOrWhiteSpace(s_idScope) && (args.Length > 0))
49-
{
50-
s_idScope = args[0];
51-
}
52-
53-
if (string.IsNullOrWhiteSpace(s_idScope))
54-
{
55-
Console.WriteLine("ProvisioningDeviceClientX509 <IDScope>");
56-
return 1;
57-
}
58-
59-
X509Certificate2 certificate = LoadProvisioningCertificate();
60-
61-
using (var security = new SecurityProviderX509Certificate(certificate))
62-
63-
// Select one of the available transports:
64-
// To optimize for size, reference only the protocols used by your application.
65-
using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly))
66-
// using (var transport = new ProvisioningTransportHandlerHttp())
67-
// using (var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.TcpOnly))
68-
// using (var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.WebSocketOnly))
69-
{
70-
ProvisioningDeviceClient provClient =
71-
ProvisioningDeviceClient.Create(GlobalDeviceEndpoint, s_idScope, security, transport);
72-
73-
var sample = new ProvisioningDeviceClientSample(provClient, security);
74-
sample.RunSampleAsync().GetAwaiter().GetResult();
75-
}
76-
77-
return 0;
78-
}
79-
80-
private static X509Certificate2 LoadProvisioningCertificate()
81-
{
82-
string certificatePassword = ReadCertificatePassword();
83-
84-
var certificateCollection = new X509Certificate2Collection();
85-
certificateCollection.Import(s_certificateFileName, certificatePassword, X509KeyStorageFlags.UserKeySet);
86-
87-
X509Certificate2 certificate = null;
88-
89-
foreach (X509Certificate2 element in certificateCollection)
90-
{
91-
Console.WriteLine($"Found certificate: {element?.Thumbprint} {element?.Subject}; PrivateKey: {element?.HasPrivateKey}");
92-
if (certificate == null && element.HasPrivateKey)
17+
// Parse application parameters
18+
Parameters parameters = null;
19+
ParserResult<Parameters> result = Parser.Default.ParseArguments<Parameters>(args)
20+
.WithParsed(parsedParams =>
9321
{
94-
certificate = element;
95-
}
96-
else
22+
parameters = parsedParams;
23+
})
24+
.WithNotParsed(errors =>
9725
{
98-
element.Dispose();
99-
}
100-
}
26+
Environment.Exit(1);
27+
});
10128

102-
if (certificate == null)
103-
{
104-
throw new FileNotFoundException($"{s_certificateFileName} did not contain any certificate with a private key.");
105-
}
106-
else
107-
{
108-
Console.WriteLine($"Using certificate {certificate.Thumbprint} {certificate.Subject}");
109-
}
29+
var sample = new ProvisioningDeviceClientSample(parameters);
30+
await sample.RunSampleAsync();
11031

111-
return certificate;
112-
}
113-
114-
private static string ReadCertificatePassword()
115-
{
116-
var password = new StringBuilder();
117-
Console.WriteLine($"Enter the PFX password for {s_certificateFileName}:");
118-
119-
while(true)
120-
{
121-
ConsoleKeyInfo key = Console.ReadKey(true);
122-
if (key.Key == ConsoleKey.Backspace)
123-
{
124-
if (password.Length > 0)
125-
{
126-
password.Remove(password.Length - 1, 1);
127-
Console.Write("\b \b");
128-
}
129-
}
130-
else if (key.Key == ConsoleKey.Enter)
131-
{
132-
Console.WriteLine();
133-
break;
134-
}
135-
else
136-
{
137-
Console.Write('*');
138-
password.Append(key.KeyChar);
139-
}
140-
}
141-
142-
return password.ToString();
32+
return 0;
14333
}
14434
}
14535
}

0 commit comments

Comments
 (0)