Azure/azure-iot-sdk-csharp

Error: No credentials are available in the security package when using PEM files.

ksaye opened this issue · 3 comments

ksaye commented

Context

  • OS, version, SKU and CPU architecture used: Version 10.0.19043.1165 on AMD64
  • Application's .NET Target Framework : net5.0
  • Device: Laptop
  • SDK version used:
    <PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.37.2" />
    <PackageReference Include="Microsoft.Azure.Devices.Provisioning.Client" Version="1.17.1" />
    <PackageReference Include="Microsoft.Azure.Devices.Provisioning.Transport.Mqtt" Version="1.15.1" />
    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="5.0.1" />

Description of the issue

Getting the error "No credentials are available in the security package" when importing PEM files. If I create a X509Certificate2 object from a PEM file (new feature in .Net 5.0), export it to a PFX file and then create an X509Cerfificate2 from that object, it works.

Note the error here if I just import the certificate from PEM:
Error

Note the success if I save to PFX and then import:
Works

Code sample exhibiting the issue

using Microsoft.Azure.Devices.Provisioning.Client;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Azure.Devices.Provisioning.Client.Transport;
using System.Security.Cryptography.X509Certificates;
using System;
using Microsoft.Azure.Devices.Client;
using System.Text;
using System.IO;

namespace DPSBug
{
    class Program
    {
        private static string dspScopeID = "0neXXXXXX";
        private static string dspURL = "global.azure-devices-provisioning.net";
        const string publicKeyFileName = "device.crt";
        const string privateKeyFileName = "device.key";

        static void Main(string[] args)
        {
            var certificate = X509Certificate2.CreateFromPemFile(publicKeyFileName, privateKeyFileName);

            /* why is this section needed? */
            byte[] exportCertificate = certificate.Export(X509ContentType.Pfx, "1234");
            File.WriteAllBytes("export.pfx", exportCertificate);
            certificate = new X509Certificate2(@"export.pfx", "1234");
            /* why is this section needed? */

            var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.TcpOnly);
            var security = new SecurityProviderX509Certificate(certificate);
            ProvisioningDeviceClient provClient =
                    ProvisioningDeviceClient.Create(dspURL, dspScopeID, security, transport);
            var result = provClient.RegisterAsync().Result;

            var auth = new DeviceAuthenticationWithX509Certificate(result.DeviceId.ToString(), certificate);
            var protocol2 = TransportType.Mqtt;
            DeviceClient client = DeviceClient.Create(result.AssignedHub.ToString(), auth, protocol2);
            client.OpenAsync().Wait();
            client.SendEventAsync(new Message(Encoding.UTF8.GetBytes("Hello"))).Wait();
            client.CloseAsync().Wait();

            Console.WriteLine("Hello World!");
        }
    }
}

Thanks for reporting @ksaye I will investigate.

ksaye commented

Closing this issue as it is an issue with SCHANNEL in Windows: dotnet/runtime#23749 (comment). Linux does not show the issue and in code it can be addressed with the following simple command:

certificate = new X509Certificate2(certificate.Export(X509ContentType.Pfx));