Search code examples
azureauthenticationamqpsaslazure-iot-hub

Connecting using shared access policy and key to IoT Hub using AMQP lite


Following the samples on the AMQP lite github page, I have successfully connected using SASL ANONYMOUS then token on $cbs endpoint. (https://github.com/Azure/amqpnetlite/blob/master/docs/articles/service_to_iothub.md)

Generating SAS tokens on every request is redundant and I would like to use the shared access policy and key as user and password when connecting to IoT Hub. I have followed the documentation at https://github.com/Azure/amqpnetlite/blob/master/docs/articles/building_application.md#specifying-an-address together with the indications on https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#protocol-specifics , but i keep getting some unauthenticated error with code "Sys".

Credentials are certainly good as I use them to generate SAS tokens and I've tried variations of the username (with/without domain) with no success. I have specifically checked that username and password are URL encoded.

Following the documentation on SASL standard, the error code Sys seems to indicate some issue on the receiver side (http://www.rfc-base.org/txt/rfc-3206.txt page 3).

Has anyone else hit this problem? Is there any solution or it is possible to connect to IoT Hub this way?

Thanks, George


Solution

  • I get "amqp:unauthorized-access" error due to without sending put-token message:

            string sasToken = GetSharedAccessSignature(null, device_key, resourceUri, new TimeSpan(1, 0, 0));
            bool cbs = PutCbsToken(connection, host, sasToken, audience);
    

    I test on Windows 10 desktop with the following code, it works for both sending and receiving:

        static string host = "[IOT_HUB_NAME].azure-devices.net";
        static int port = 5671;
        static string device_id = "[DEVICE_ID]";
        static string device_key = "[DEVICE_KEY]";
        static Session session;
    
        static void Main(string[] args)
        {
            Address address = new Address(host, port, null, null);
            Connection connection = new Connection(address);
    
            string audience = Fx.Format("{0}/devices/{1}", host, device_id);
            string resourceUri = Fx.Format("{0}/devices/{1}", host, device_id);
    
            string sasToken = GetSharedAccessSignature(null, device_key, resourceUri, new TimeSpan(1, 0, 0));
            bool cbs = PutCbsToken(connection, host, sasToken, audience);
    
            session = new Session(connection);
    
            SendEvent();
            ReceiveCommands();
            Console.ReadLine();
        }
    
        static private void SendEvent()
        {
            string entity = Fx.Format("/devices/{0}/messages/events", device_id);
    
            SenderLink senderLink = new SenderLink(session, "sender-link", entity);
    
            var messageValue = Encoding.UTF8.GetBytes("i am a message.");
            Message message = new Message()
            {
                BodySection = new Data() { Binary = messageValue }
            };
    
            senderLink.Send(message);
            senderLink.Close();
        }
    
        static private void ReceiveCommands()
        {
            string entity = Fx.Format("/devices/{0}/messages/deviceBound", device_id);
    
            ReceiverLink receiveLink = new ReceiverLink(session, "receive-link", entity);
    
            Message received = receiveLink.Receive();
            if (received != null)
                receiveLink.Accept(received);
            Console.WriteLine(received.BodySection.ToString());
            receiveLink.Close();
        }
    

    For more information you can reference "CONNECTING TO THE AZURE IOT HUB USING AN AMQP STACK".

    Update:

    Instead of using SAS token like above code piece, the following code use shared access policy and key(SASL PLAIN):

        static string host = "[IOT_HUB_NAME].azure-devices.net";
        static int port = 5671;
        static string device_id = "[DEVICE_ID]";
        static string device_key = "[DEVICE_KEY]";
        private const string username_hublevel = "iothubowner@sas.root.[IOT_HUB_NAME]";
        private const string password_hublevel = "SharedAccessSignature sr={URL-encoded-resourceURI}&sig={signature-string}&se={expiry}&skn={policyName}";
    
        static Session session;
    
        static void Main(string[] args)
        {
            Address address = new Address(host, port, username_hublevel, password_hublevel);
            Connection connection = new Connection(address);
    
            string audience = Fx.Format("{0}/devices/{1}", host, device_id);
            string resourceUri = Fx.Format("{0}/devices/{1}", host, device_id);
    
            session = new Session(connection);
    
            SendEvent();
            Console.WriteLine("Sent Hello AMQP!");
            ReceiveCommands();
            Console.ReadLine();
        }