Home > Software design >  Group enrollment in Azure Device Provisoning Service (DPS) not working through REST API: unauthorize
Group enrollment in Azure Device Provisoning Service (DPS) not working through REST API: unauthorize

Time:05-07

I am trying to support DPS for my ESP32 firmware through HTTPS REST API using SAS.

My device registration ID is: xx-xx-8c4b14149ff4

I took the group enrollment from DPS primary key to generate the symmetric key from the registration ID.

I created the related SAS from that and forged the request, but the server returns "Unauthorized" with error code being 401002.

Here is my request (a curl version is provided for handiness ):

curl -L -i -X PUT \
-H 'Content-Type: application/json' \
-H 'Content-Encoding: utf-8' \
-H 'Authorization: SharedAccessSignature sr=0neXXXXXX22/registrations%xx-xx-8c4b14149ff4&sig=XXXXXXXXXXXXXXXXXXX=&skn=registration&se=1651482003' \
-d '{"registrationId": "xx-xx-8c4b14149ff4"}' \
https://global.azure-devices-provisioning.net/0neXXXXXX22/registrations/xx-xx-8c4b14149ff4/register?api-version=2021-06-01

Note that I replaced secret information with "xx".

The response body is as follows:

{
    "errorCode": 401002,
    "trackingId": "9fecada7-4e51-455e-9392-68522654a64a",
    "message": "Unauthorized",
    "timestampUtc": "2022-05-02T08:04:03.5761437Z"
}

Is there anything I must tweak to use the HTTPS REST API from the portal?

What should I look at beside the information themselves (which I already double-checked)?

References:

CodePudding user response:

Can you provide a code sample of how you are generating the individual key? Did you try the samples at https://docs.microsoft.com/en-us/azure/iot-dps/how-to-legacy-device-symm-key?tabs=linux" \l "derive-a-device-key#derive-a-device-key ?

Here's an example using C#

        private string GenerateDeviceSas(string enrollmentGroupName, string dpsIdScope, string registrationId, string key)
        {
            if(String.IsNullOrEmpty(dpsIdScope) || String.IsNullOrEmpty(registrationId) || String.IsNullOrEmpty(key))
            {
                Console.WriteLine("Error: Missing required values in settings.json");
                return null;
            }

            if (String.IsNullOrEmpty(enrollmentGroupName))
            {
                // Generate Device API SAS key for individual enrollment
                return GenerateSasToken($"{dpsIdScope}/registrations/{registrationId}", key, "registration");
            }
            else
            {

                // Generate derived Device API SAS key for group enrollment
                // See https://docs.microsoft.com/en-us/azure/iot-dps/how-to-legacy-device-symm-key?tabs=linux#create-a-symmetric-key-enrollment-group

                HMACSHA256 hmacsha256 = new HMACSHA256();
                hmacsha256.Key = Convert.FromBase64String(key);
                var sig = hmacsha256.ComputeHash(ASCIIEncoding.ASCII.GetBytes(registrationId));
                var derivedkey = Convert.ToBase64String(sig);

                return GenerateSasToken($"{dpsIdScope}/registrations/{registrationId}", derivedkey, "registration");

            }
        }

        private static string GenerateSasToken(string resourceUri, string key, string policyName, int expiryInSeconds = 3600)
        {
            TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);
            string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds   expiryInSeconds);

            string stringToSign = WebUtility.UrlEncode(resourceUri)   "\n"   expiry;

            HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key));
            string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));

            string token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", WebUtility.UrlEncode(resourceUri), WebUtility.UrlEncode(signature), expiry);

            if (!String.IsNullOrEmpty(policyName))
            {
                token  = "&skn="   policyName;
            }

            return token;
        }

CodePudding user response:

Consider the following and note how I calculate a deviceKey from the DPS key and then generate the SAS Token from the device key. Some people overlook that. :)

from base64 import b64encode, b64decode
from hashlib import sha256
from time import time
from urllib.parse import urlencode, quote_plus
from hmac import HMAC
import requests

def generate_sas_token(uri, key, policy_name, expiry=3600):
    ttl = time()   expiry
    sign_key = "%s\n%d" % ((quote_plus(uri)), int(ttl))
    sign_key = sign_key.encode('utf-8')
    signature = b64encode(HMAC(b64decode(key), sign_key, sha256).digest())

    rawtoken = {
        'sr' :  uri,
        'sig': signature,
        'se' : str(int(ttl))
    }
    
    if policy_name:
        rawtoken['skn'] = policy_name

    return 'SharedAccessSignature '   urlencode(rawtoken)

device_id = "3c71bfa95f7c"
scope_id = "0neYOURSCOPEHERE39"
dpskey = 'CsBYOURSASCODEHEREXWyfUMl/OA=='

deviceKey = b64encode(HMAC(b64decode(dpskey), device_id.encode('utf-8'), sha256).digest())
uri = scope_id   '/registrations/'   device_id
policy= 'registration'
url = "https://global.azure-devices-provisioning.net/"   scope_id   "/registrations/"   device_id   "/register?api-version=2021-06-01"
headers = {'Authorization': generate_sas_token(uri=uri, key=deviceKey, policy_name=policy), 'User-Agent': 'MicroPython', 'content-type': 'application/json', 'Content-Encoding': 'utf-8'}
data = '{"registrationId" : "'   device_id   '"}'

print(generate_sas_token(uri=uri, key=deviceKey, policy_name=policy))

print(requests.request("PUT", url=url, data=data, headers=headers).json())

The response shows "assigning", which is success. Hope this helps.

C:/Python38-64/python.exe h:/test/sastoken.py
SharedAccessSignature sr=0ne00223A39/registrations/3c71bfa95f7c&sig=UH5KKeREMOVED0vcs=&se=1651880438&skn=registration
{'operationId': '4.4cb788ae24922c84.6856a3d0-857f-4c79-a90c-360fdf3355f8', 'status': 'assigning'}
  • Related