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 ?
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.