Search code examples
azure-iot-hubazure-iot-sdk

InvalidErrorCode when sending C2D message through IoT Hub with SAS token


I want to send a cloud to device message (C2D) to a device, which is known in IoTHub. I use the SERVICE policy in the code. The sending occurs from a Console application (for now) or later a WebApi. So the code will not run on the device!

I want to use a SAS-token to connect with expiration, instead of connecting with the symmetric key of the device. I created the following code:

A helperclass:

..
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Common.Security;
..

public static class ServiceClientSasConnectionHelper
{
    public static string GetSasConnectionString(SasTokenModel sasTokenModel)
    {
        var sasToken = GetSasToken(sasTokenModel);
        var connectionString = string.Empty;
        if (!string.IsNullOrEmpty(sasTokenModel.PolicyName))
            connectionString = IotHubConnectionStringBuilder.Create(sasTokenModel.HostName, AuthenticationMethodFactory.CreateAuthenticationWithSharedAccessPolicyToken(sasTokenModel.PolicyName, sasToken)).ToString();
        else
            connectionString = $"HostName={sasTokenModel.HostName};DeviceId={sasTokenModel.DeviceId};SharedAccessSignature={sasToken}";
        return connectionString;
    }

    private static string GetSasToken(SasTokenModel sasTokenModel)
    {
        var sasBuilder = new SharedAccessSignatureBuilder
        {
            Key = sasTokenModel.SigninKey,
            Target = $"{sasTokenModel.HostName}/devices/{sasTokenModel.DeviceId}",
            TimeToLive = TimeSpan.FromMinutes(sasTokenModel.TokenLifeTimeInMinutes)
        };

        var sasToken = sasBuilder.ToSignature();

        if (!string.IsNullOrEmpty(sasTokenModel.PolicyName))
            sasToken += $"&skn={sasTokenModel.PolicyName}";

        return sasToken;
    }
}

It is called from PROGRAM.CS:

var policyName = string.Empty;
policyName = ConfigHelper.GetPolicyName();  //service
var hostName = ConfigHelper.GetIotUri(); //companyname-Tenant2Display.azure-devices.net
var policyKey = ConfigHelper.GetPolicyKey(); // primarary key from Portal

var sasTokenModel = new SasTokenModel
{
    DeviceId = WebUtility.UrlEncode(deviceKeyPair.Id), //DeviceId
    SigninKey = string.IsNullOrEmpty(policyName) ? deviceKeyPair.Key : policyKey,
    HostName = hostName,
    PolicyName = policyName,
    TokenLifeTimeInMinutes = 5
};
var connString = ServiceClientSasConnectionHelper.GetSasConnectionString(sasTokenModel);
var serviceClient = ServiceClient.CreateFromConnectionString(connString);
await serviceClient.SendAsync(deviceId, commandMessage);

When calling the SendAsync I receive an error, with the following message:

\r\nTracking Id:fee6f860bdff42faa7cad8f81095223e-G:10-TimeStamp:04/19/2017 21:09:32

In de exception at property Code, the value is: Microsoft.Azure.Devices.Common.Exceptions.ErrorCode.InvalidErrorCode

Stacktrace:

at Microsoft.Azure.Devices.AmqpServiceClient.d__28.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at TelemetrySender.Service.Sender.d__1.MoveNext() in C:\repo\TelemetrySender.cs:line 34

I looked into the Github repository "azure-iot-sdk-csharp" in de service section, but I cannot find an reason for this error. I do see where the error message is set, but I dont understand why.

Anyone who can help me ?


Solution

  • I want to send a cloud to device message (C2D) to a device... I want to use a SAS-token to connect with expiration, instead of connecting with the symmetric key of the device

    You can't send Cloud-To-Device message using device-level credentials. You need use IoT hub-level credentials.

    So, you need remove /devices/{sasTokenModel.DeviceId} in Target of GetSasToken(). The final SAS-token will have this format:

    SharedAccessSignature sig={signature-string}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}
    

    I can't reproduce your issue. I test on desktop and OS version is 10.0.14393.

    But I can send Cloud-To-Device message successfully using your in-complete code and the corrective SAS-token format(Previously mentioned). You can have a reference.