Forum Discussion

DamianLB's avatar
DamianLB
Copper Contributor
Aug 07, 2023

IotHubUnauthorizedAccess only when using X.509 Cert to GetFileUploadSasUriAsync

I'm creating a POC for our company using the .NET SDK for Azure IoT. 

 

I'm coming up against a problem when performing an HTTP upload, but only when using an X509 cert.  

I'm provisioning the device using DPS, group enrollment. Following enrollment, I can send data, update twins and do everything besides uploading a file. 

 

Using the same code, the HTTP Upload functions correctly when using a SymmetricKey (in my POC, provisioning is interchangeable between key and cert). It's only when using the X509 cert, which works perfectly fine for all other operations. 

 

GetFileUploadSasUriAsync is the method which generates the error: 

 

{"Message":"ErrorCode:IotHubUnauthorizedAccess;Unauthorized","ExceptionMessage":"Tracking ID:2d210xxxxxxxxe3afdc-G:0-TimeStamp:08/04/2023 15:48:25"}

   at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.<ExecuteAsync>d__22.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.<PostAsync>d__18`2.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Devices.Client.Transport.HttpTransportHandler.<GetFileUploadSasUriAsync>d__15.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at RemoteIoTDeviceConsoleApp.Class.Data.FileOperations.<UploadFile>d__3.MoveNext() in E:\zzzz\xxxx\xxx\xxx\Class\Data\FileOperations.cs:line 53

 

How I'm initialising the device client. 

 

 

public static string DeviceName;
        
public static DeviceClient IoTDeviceClient;

public static LocalConfigStore ConfigStore = new LocalConfigStore();  

public static void InitialiseIoTClient()
        {
            Console.Clear();

            if (ConfigStore.CheckConfigFileExists())
            {
                var storedConfig = ConfigStore.ReadConfigFile();

                DeviceName = storedConfig.DeviceName;

                if (IoTDeviceClient == null)
                {
                    if (storedConfig.AuthType == AuthType.X509)
                    {
                        var certPath = AppDomain.CurrentDomain.BaseDirectory + storedConfig.CertName;

                        Console.WriteLine($"Loading the certificate...");
                        using X509Certificate2 certificate = new X509Certificate2(certPath, storedConfig.CertPassword);
                        using var security = new SecurityProviderX509Certificate(certificate);
                        using var auth = new DeviceAuthenticationWithX509Certificate(storedConfig.DeviceName, certificate);
                        IoTDeviceClient = DeviceClient.Create(storedConfig.AssignedIoTHub, auth, TransportType.Mqtt);
                        IoTDeviceClient.SetRetryPolicy(new ExponentialBackoff(5, new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 15)));
                        IoTDeviceClient.OpenAsync().Wait();
                    }
                    else
                    {
                        IoTDeviceClient = DeviceClient.Create(storedConfig.AssignedIoTHub, new DeviceAuthenticationWithRegistrySymmetricKey(storedConfig.DeviceName, storedConfig.DevicePrimaryKey), TransportType.Mqtt);
                        IoTDeviceClient.SetRetryPolicy(new ExponentialBackoff(5, new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 15)));
                        IoTDeviceClient.OpenAsync().Wait();
                    }


                }
            }
            else
            {
                Console.WriteLine("No config file exists, register a player - InitialiseIoTClient");
            }

        }

 

 

How I'm uploading a file. 

 

 

 

 public class FileOperations
    {
        readonly DeviceClient _deviceClient;

        public static LocalConfigStore ConfigStore = new LocalConfigStore();

        /// <summary>
        /// Load the client and start listening for file upload notifications
        /// </summary>
        internal FileOperations()
        {
            _deviceClient = Program.IoTDeviceClient;

            ReceiveFileUploadNotificationAsync();
        }

        /// <summary>
        ///  This method is used to upload a file to the IoT Hub
        /// </summary>
        public async void UploadFile()
        {
            // Generate a large file to upload
            var commonOps = new Utils.Common();
            var testFilePath = commonOps.GenerateLargeFile();

            // Create stream
            using var fileStreamSource = new FileStream(testFilePath, FileMode.Open);
            var fileName = Path.GetFileName(testFilePath);

            var fileUploadTime = Stopwatch.StartNew();

            // Get the SAS URI for the file upload
            var fileUploadSasUriRequest = new FileUploadSasUriRequest
            {
                BlobName = fileName
            };
            FileUploadSasUriResponse sasUri = await _deviceClient.GetFileUploadSasUriAsync(fileUploadSasUriRequest, CancellationToken.None);
            Uri uploadUri = sasUri.GetBlobUri();

            Console.WriteLine($"Uploading file {fileName} to {uploadUri}.");

            // Upload the file to blob storage
            var blockBlobClient = new BlockBlobClient(uploadUri);
            await blockBlobClient.UploadAsync(fileStreamSource, new BlobUploadOptions());

            // Notify IoT Hub that the file upload is complete
            var successfulFileUploadCompletionNotification = new FileUploadCompletionNotification
            {
                // Mandatory. Must be the same value as the correlation id returned in the sas uri response
                CorrelationId = sasUri.CorrelationId,

                // Mandatory. Will be present when service client receives this file upload notification
                IsSuccess = true,

                // Optional, user defined status code. Will be present when service client receives this file upload notification
                StatusCode = 200,

                // Optional, user-defined status description. Will be present when service client receives this file upload notification
                StatusDescription = "Success"
            };

            // Notify IoT Hub that the file upload is complete
            await _deviceClient.CompleteFileUploadAsync(successfulFileUploadCompletionNotification);

            fileUploadTime.Stop();

            Console.WriteLine($"File upload completed successfully.");

            Console.WriteLine($"Time to upload file: {fileUploadTime.Elapsed}.");

            Console.WriteLine($"Deleting Test File.");
            
            fileStreamSource.Dispose();

            File.Delete(testFilePath);

            Console.WriteLine("press enter to continue.");
    
        }

        /// <summary>
        ///  This method is used to receive file upload notifications
        /// </summary>
        private async void ReceiveFileUploadNotificationAsync()
        {
            var connectionString = ConfigurationManager.AppSettings["ConnectionStringIotHubOwner"];

            // Create service client to receive file upload notifications
            ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(connectionString);

            // Get file upload notification receiver
            var notificationReceiver = serviceClient.GetFileNotificationReceiver();

            Console.WriteLine("\nReceiving file upload notification from service");
            while (true)
            {
                // Wait for file upload notification
                var fileUploadNotification = await notificationReceiver.ReceiveAsync();
                if (fileUploadNotification == null) continue;
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Received file upload notification: {0}", string.Join(", ", fileUploadNotification.BlobName));
                Console.ResetColor();
                await notificationReceiver.CompleteAsync(fileUploadNotification);
            }
        }
        
    }

 

 

 

1 Reply

  • DamianLB's avatar
    DamianLB
    Copper Contributor

    For anyone else who has the same issue in the future, this is how I fixed it. 

     

    This code

     

     using X509Certificate2 certificate = new X509Certificate2(certPath, storedConfig.CertPassword);
                            using var security = new SecurityProviderX509Certificate(certificate);
                            using var auth = new DeviceAuthenticationWithX509Certificate(storedConfig.DeviceName, certificate);
                            IoTDeviceClient = DeviceClient.Create(storedConfig.AssignedIoTHub, auth, TransportType.Mqtt);
                            IoTDeviceClient.SetRetryPolicy(new ExponentialBackoff(5, new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 15)));
                            IoTDeviceClient.OpenAsync().Wait();

     

    Changed to: 

     

                            SecureString theSecureString = new NetworkCredential("", storedConfig.CertPassword).SecurePassword;
                            var cert = new X509Certificate2(certPath, theSecureString);
                            var auth = new DeviceAuthenticationWithX509Certificate(storedConfig.DeviceName, cert);
                            IoTDeviceClient = DeviceClient.Create(storedConfig.AssignedIoTHub, auth);
                            IoTDeviceClient.SetRetryPolicy(new ExponentialBackoff(5, new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 30), new TimeSpan(0, 1, 15)));
                            IoTDeviceClient.OpenAsync().Wait();

     

    I can now use IoT HTTP upload. 

Resources