Pre-authorisation with TPM endorsement key

I’m trying to connect DPS and Mender (with custom allocation policy) like explained in this vidéo :

Over-the-air software updates for Azure IoT Hub with Mender.io - YouTube

Thing is I have just an endorsement key in my TPM chip and not an X509 Certificate, the endorsement key is RSA, I’ve used bouncy castle (.NET) to générate a PEM format of this key but when I make a request for preauthorization I’m getting a BAD Request :
I’m calling this url :

https://hosted.mender.io/api/management/v2/devauth/devices

and the body :

dynamic body = new JObject();
body.pubkey = publicKeyPEM;
body.identity_data = new JObject();
body.identity_data.DeviceId = regId;

Have you any idea what could be wrong ?

What method of authentication are you trying to use to log into the management apis?

I’m using Basic authentication,
here is my code :

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using Org.BouncyCastle.Utilities.IO.Pem;
using Newtonsoft.Json.Linq;
using System.Text;
using System.Net.Http.Headers;

namespace PreAuthMenderFunction
{
    public static class PreAuthMenderFunction
    {
        [FunctionName("PreAuthMenderFunction")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
           

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);

            string endorsementKey = data?.deviceRuntimeContext?.tpm?.endorsementKey;

            log.LogInformation("Request.Body:...");
            log.LogInformation(requestBody);
            log.LogInformation(endorsementKey);

            // Get registration ID of the device
            string regId = data?.deviceRuntimeContext?.registrationId;

            log.LogInformation(regId);


            //Login to mender 
            string credentials = Environment.GetEnvironmentVariable("mender-secret", EnvironmentVariableTarget.Process);

            var loginUrl = new Uri($"https://hosted.mender.io/api/management/v1/useradm/auth/login");

            string JWT;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", $"Basic {credentials}");
                var result = client.PostAsync($"{loginUrl}", null).Result;
                log.LogInformation("Login status code : " + result.StatusCode);
                log.LogInformation("Is success : " + result.IsSuccessStatusCode);
                log.LogInformation("Login error message : " + result.ReasonPhrase);

                //JWT = result.Content.ReadAsStringAsync().Result.Trim('"'); //get  JWT for call funtion key

                log.LogInformation(result.Content.Headers.ToString());
                log.LogInformation("JWT2 Token:");
                var task = result.Content.ReadAsStringAsync();
                task.Wait();
                
                log.LogInformation(task.Result);
                JWT = task.Result;
                log.LogInformation(JWT);
            }

            //pre-authorize in mender 
            String publicKeyPEM = "";

            try
            {
                TextWriter textWriter = new StringWriter();
                PemWriter pemWriter = new PemWriter(textWriter);
                PemObject obj = new PemObject("PUBLIC KEY", Convert.FromBase64String(endorsementKey));
                pemWriter.WriteObject(obj);
                pemWriter.Writer.Flush();
                publicKeyPEM = textWriter.ToString();

            }
            catch(Exception e)
            {
                log.LogInformation("Une erreur s'est passé : " + e.Message);
            }

            log.LogInformation("format de la public key en PEM");
            log.LogInformation(publicKeyPEM);
            log.LogInformation(regId);

            //CONNECTION

            var preAuthUrl = new Uri($"https://hosted.mender.io/api/management/v2/devauth/devices");

            dynamic body = new JObject();
            body.pubkey = publicKeyPEM;
            body.identity_data = new JObject();
            body.identity_data.DeviceId = regId;

            log.LogInformation("---------------param mender--------------------");

            log.LogInformation((string)body.pubkey);
            log.LogInformation((string)body.identity_data.DeviceId);

            log.LogInformation("---------------body--------------------");
            log.LogInformation(regId);

            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage(HttpMethod.Post, preAuthUrl))
            using (var httpContent = CreateHttpContent(body))
            {
                request.Content = httpContent;

                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + JWT);
                var response = await client.SendAsync(request);

                if (!response.IsSuccessStatusCode)
                {
                    log.LogInformation("mender preauth error");
                    log.LogInformation(response.StatusCode.ToString());
                    log.LogInformation(response.ReasonPhrase);
                }
               
            }

            string iot = "min-ds-dev-iothub-free.azure-devices.net";

            return new OkObjectResult(new ResponseObj() { iotHubHostName = iot });

        }

        public static HttpContent CreateHttpContent(object content)
        {
            HttpContent httpContent = null;

            if (content != null)
            {
                var ms = new MemoryStream();
                SerializeJsonIntoStream(content, ms);
                ms.Seek(0, SeekOrigin.Begin);
                httpContent = new StreamContent(ms);
                httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }

            return httpContent;
        }

        public static void SerializeJsonIntoStream(object value, Stream stream)
        {
            using (var sw = new StreamWriter(stream, new UTF8Encoding(false), 1024, true))
            using (var jtw = new JsonTextWriter(sw) { Formatting = Formatting.None })
            {
                var js = new JsonSerializer();
                js.Serialize(jtw, value);
                jtw.Flush();
            }
        }
    }

   

    public class ResponseObj
    {       
        public string iotHubHostName { get; set; }
    }
}

And the logs (in Azure) :

2022-06-24T09:17:22.684 [Information] Content-Type: application/jwtContent-Length: 1105

2022-06-24T09:17:22.684 [Information] JWT2 Token:

2022-06-24T09:17:22.684 [Information] eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0OTM1MWQwYy04MjViLTRmZTgtYjNjOC01Y2NmZjM1MzM1MTIiLCJzdWIiOiI3MmZkYTlmYS0xYTNjLTQzOWItOTI4MS0wNDMzNTQwMmZhYTkiLCJleHAiOjE2NTY2NjcwNDIsImlhdCI6MTY1NjA2MjI0MiwibWVuZGVyLnRlbmFudCI6IjYyNDQ0YTM5MjY2OTQ1ODUwZTMyZDhjMyIsIm1lbmRlci51c2VyIjp0cnVlLCJpc3MiOiJNZW5kZXIgVXNlcnMiLCJzY3AiOiJtZW5kZXIuKiIsIm1lbmRlci5wbGFuIjoiZW50ZXJwcmlzZSIsIm1lbmRlci50cmlhbCI6dHJ1ZSwibWVuZGVyLmFkZG9ucyI6W3sibmFtZSI6ImNvbmZpZ3VyZSIsImVuYWJsZWQiOnRydWV9LHsibmFtZSI6InRyb3VibGVzaG9vdCIsImVuYWJsZWQiOnRydWV9LHsibmFtZSI6Im1vbml0b3IiLCJlbmFibGVkIjp0cnVlfV0sIm5iZiI6MTY1NjA2MjI0Mn0.v782Jg560ccCCFbx8sk7JjXrRxEqoVviNofDv6NUbcaEUTAiPDJ_zZcMwLAA-CeNjMnHkMgO-JqAGTjp6pd9F8QQUciV68S9AUhG6DQc8ch7BbsYqW3gWNd0NvhJyP18fgdo9jvqbO6n6-yUgzJ9pV0CFhMRL1XtBaOqUgJLQLMl9CVWWbxiBI_xMiOs2b6FuAaZzDxflnzykVOmjRRukTcfqU9CpTaWjpNcA2KEbIs7mixbNEzKwD0R5YQ-EQCALb17GZbuXxg1HtyiDDA6GLFURiVsxSWHqqo-BOoVgIydDQfZzNgh2fBqg1e-VqYIxcvfb3IvT-UPXnqjIFWZLdm8kQ6zqn2cjY0FptfdFtDM1Kmd2OqYkj19dnWyeYzEwVZ1SRapCGAQ9Zw2EeL7WCFgHV_DlmUOlZU1qbfW2eWekCDCKtCULvfOTygExtBkHvCo7q1dnx-dzbmEWngulpPFviy2aETcDOeJf1MeMcscWT1k9au3eykBwE-M0WUb

2022-06-24T09:17:22.685 [Information] eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0OTM1MWQwYy04MjViLTRmZTgtYjNjOC01Y2NmZjM1MzM1MTIiLCJzdWIiOiI3MmZkYTlmYS0xYTNjLTQzOWItOTI4MS0wNDMzNTQwMmZhYTkiLCJleHAiOjE2NTY2NjcwNDIsImlhdCI6MTY1NjA2MjI0MiwibWVuZGVyLnRlbmFudCI6IjYyNDQ0YTM5MjY2OTQ1ODUwZTMyZDhjMyIsIm1lbmRlci51c2VyIjp0cnVlLCJpc3MiOiJNZW5kZXIgVXNlcnMiLCJzY3AiOiJtZW5kZXIuKiIsIm1lbmRlci5wbGFuIjoiZW50ZXJwcmlzZSIsIm1lbmRlci50cmlhbCI6dHJ1ZSwibWVuZGVyLmFkZG9ucyI6W3sibmFtZSI6ImNvbmZpZ3VyZSIsImVuYWJsZWQiOnRydWV9LHsibmFtZSI6InRyb3VibGVzaG9vdCIsImVuYWJsZWQiOnRydWV9LHsibmFtZSI6Im1vbml0b3IiLCJlbmFibGVkIjp0cnVlfV0sIm5iZiI6MTY1NjA2MjI0Mn0.v782Jg560ccCCFbx8sk7JjXrRxEqoVviNofDv6NUbcaEUTAiPDJ_zZcMwLAA-CeNjMnHkMgO-JqAGTjp6pd9F8QQUciV68S9AUhG6DQc8ch7BbsYqW3gWNd0NvhJyP18fgdo9jvqbO6n6-yUgzJ9pV0CFhMRL1XtBaOqUgJLQLMl9CVWWbxiBI_xMiOs2b6FuAaZzDxflnzykVOmjRRukTcfqU9CpTaWjpNcA2KEbIs7mixbNEzKwD0R5YQ-EQCALb17GZbuXxg1HtyiDDA6GLFURiVsxSWHqqo-BOoVgIydDQfZzNgh2fBqg1e-VqYIxcvfb3IvT-UPXnqjIFWZLdm8kQ6zqn2cjY0FptfdFtDM1Kmd2OqYkj19dnWyeYzEwVZ1SRapCGAQ9Zw2EeL7WCFgHV_DlmUOlZU1qbfW2eWekCDCKtCULvfOTygExtBkHvCo7q1dnx-dzbmEWngulpPFviy2aETcDOeJf1MeMcscWT1k9au3eykBwE-M0WUb

2022-06-24T09:17:22.689 [Information] format de la public key en PEM

2022-06-24T09:17:22.689 [Information] -----BEGIN PUBLIC KEY-----AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAl7wxkgjflIV5I7b4hicWAcCvoGZOkQYoPp8C5N8HDZPGqsQRQ0TkIY/VIKOwxMgSa7rJrmhsam/9UT3dZxHRYo4WSchKdV8aIe4U/syRmZmKTfQ+MGpbng5GKfrVqtnw3h0e8vPTgodCKAmT2uIil1A8XGSeFED8mlbsDjUO2Ga5WHWT3XNhujsECYWn8JPYzAk07EPBcQrDqVzScOFXWmaQ0dmF80QQ6L5EVwfO2cxnJ5PnRz8jfmbTIQ7q1wgzgeFYqHtXuKmYah0j+UY1ntPnOclbzprlW/IYVNOmDQYsK8kAv5dT1CKze0WS+dwAtyTS+108kwLNWFnt2WuQ2Q==-----END PUBLIC KEY-----

2022-06-24T09:17:22.689 [Information] tpmmenderpoc

2022-06-24T09:17:22.695 [Information] ---------------param mender--------------------

2022-06-24T09:17:22.705 [Information] -----BEGIN PUBLIC KEY-----AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAl7wxkgjflIV5I7b4hicWAcCvoGZOkQYoPp8C5N8HDZPGqsQRQ0TkIY/VIKOwxMgSa7rJrmhsam/9UT3dZxHRYo4WSchKdV8aIe4U/syRmZmKTfQ+MGpbng5GKfrVqtnw3h0e8vPTgodCKAmT2uIil1A8XGSeFED8mlbsDjUO2Ga5WHWT3XNhujsECYWn8JPYzAk07EPBcQrDqVzScOFXWmaQ0dmF80QQ6L5EVwfO2cxnJ5PnRz8jfmbTIQ7q1wgzgeFYqHtXuKmYah0j+UY1ntPnOclbzprlW/IYVNOmDQYsK8kAv5dT1CKze0WS+dwAtyTS+108kwLNWFnt2WuQ2Q==-----END PUBLIC KEY-----

2022-06-24T09:17:22.706 [Information] tpmmenderpoc

2022-06-24T09:17:22.706 [Information] ---------------body--------------------

2022-06-24T09:17:22.706 [Information] tpmmenderpoc

2022-06-24T09:17:23.094 [Information] mender preauth error

2022-06-24T09:17:23.094 [Information] BadRequest

2022-06-24T09:17:23.094 [Information] Bad Request

2022-06-24T09:17:23.095 [Information] Executed ‘PreAuthMenderFunction’ (Succeeded, Id=1fc7969b-ca7e-47a5-9996-5b081058eeb8, Duration=1413ms)

2022-06-24T09:18:39 No new trace in the past 1 min(s).

We can see the BadRequest at the end

Can you check that your PEM newlines are being escaped correctly for inclusion in JSON as per mender doc

Thank you for your reactivity.
Before investigating too much, I know that you are collaborating with Microsoft, do you have already made an integration with Azure DPS using TPM ?
Or saying differently, do you accept directly TPM endorsement key (public part) ?
If yes, I will continue (to work with TPM in C# we must use TSS.NET and not just System.Cryptography).
Azure IoT simply use TSS.NET
It will be great if the answer is yes to have a small sample if possible.

I’ll leave that one for the Mender team to answer, in the meantime, it’s worth double-checking the JSON in your request as this can be a source of bad requests, as I believe the PEM content needs newline escaping in the JSON is one such cause. Unsupported Key type can also be another source, but it looks like you are using RSA public key which should be well-supported already.

You can close the ticket,
I had to use TSS.NET in order to export modulus and exponent (we suppose a padding of zero), and another library to export to opensslpem.
It was not straightforward, but my device is preauthorized.
Thanks a lot for your reactivity.
We are just starting using Mender so maybe we will have some other questions in the future.
Bye

1 Like