0

I'm creating an authorization flow in Microsoft Azure, currently using a Logic app (consumption), doing some code in an Azure function as well.

For the flow to work I need to create a "Digest" header. The function code for that is pasted below. This works as expected and the header is created correctly.

"Digest" creation:

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.Text;

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

            string value = req.Query["value"];

            var crypt = new System.Security.Cryptography.SHA256Managed();
            var hash = new StringBuilder();
            byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(value));
            foreach (byte theByte in crypto)
            {
                hash.Append(theByte.ToString("x2"));
            }

            var response = Convert.ToBase64String(crypto);

            return new OkObjectResult(response);
        }
    }
}

I'm also required to provide a "Signature" header. And this is where I get completely stuck.

The "Signature" header should be created like this:

*The signature headers required for all POST or PUT requests are: "(request-target) x-bank-originating-host x-bank-originating-date content-type digest":

POST: https://open.bank.com/corporate/v2/authorize X-Bank-Originating-Host: open.bank.com X-Bank-Originating-Date: Thu, 05 Jun 2019 21:31:40 GMT content-type: application/json digest: Signature: keyId="",algorithm="rsa-sha256", headers="(request-target) x-bank-originating-host x-bank-originating-date content-type digest", signature="

Where*

*<client_id> is the Client Id assigned to the TPP's client application is the hash (SHA256) digest of the request body is the BASE64 encoded version of the RSA-SHA256 encrypted signing string

The client would compose the signing string as:

HTTP (request-target): post /corporate/v2/authorize\n x-bank-originating-host: open.bank.com\n x-bank-originating-date: Thu, 05 Jun 2019 21:31:40 GMT\n content-type: application/json\n digest: SHA-256=jcC/ttW7JucGTN9hWfqMsFeON6D+vZtQGWJA+W0PL/g= (created in previous function)

Note that the '\n' symbols above are included to demonstrate where the new line character should be inserted. There is no new line on the final line of the signing string. Note also that the value for the digest shown above serves only as an example and in reality needs to be computed based on the request message body.*

How would you move forward in trying to achive this in an Azure Function in C#? Where would you upload the certificate and how would you access it in trying to create the signing header?

I've tried uploading the certificate to an Azure Key Vault, but it seems invalid, I have contacted the issuer about this. It is also an .p12 file, which is not supported in Azure Key vault. Not sure if there is an actual issue with the certificate or that something just went wrong when I changed file extension to .pfx. Still, waiting for an answer from the issuer.

Code wise - Haven't tried anything yet, as I don't know how to construct the flow itself.

NicoleB
  • 1
  • 1

1 Answers1

0

To create the "Signature" header, you need to do the following steps in your Azure Function.

Construct the signing string using the HTTP method, request target, headers, and digest value. Sign the signing string using RSA-SHA256 encryption with the private key of the certificate. Base64-encode the resulting signature. Construct the "Signature" header with the key ID, algorithm, headers, and signature.

To create a BASE64 encoded version of a RSA-SHA256 encrypted string in Azure Function C#, you need to follow these steps:

  1. Create a signing string:

The signing string should contain the following:

HTTP (request-target): post /corporate/v2/authorize\n x-bank-originating-host: open.bank.com\n x-bank-originating-date: Thu, 21 Mar 2023 21:31:40 GMT\n content-type: application/json\n digest: SHA-

Sample code

public static class CreateSignature
    {
        private static readonly string KeyId = "your_key_id_here";
        private static readonly string Algorithm = "rsa-sha256";
        private static readonly string[] Headers = new string[] { "(request-target)", "x-bank-originating-host", "x-bank-originating-date", "content-type", "digest" };
        private static readonly string CertificateThumbprint = "your_certificate_thumbprint_here";

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

            var headers = new Dictionary<string, string>();
            foreach (var header in req.Headers)
            {
                headers.Add(header.Key.ToLower(), header.Value.ToString());
            }

            var method = req.Method.ToLower();
            var path = req.Path.Value.ToLower();

            var signingString = new StringBuilder();
            signingString.Append($"{method} {path}\n");
            foreach (var header in Headers)
            {
                if (header.Equals("(request-target)"))
                {
                    signingString.Append($"(request-target): {method} {path}\n");
                }
                else if (headers.ContainsKey(header))
                {
                    signingString.Append($"{header}: {headers[header]}\n");
                }
            }

            var digest = headers["digest"].Substring(7); // Remove the "SHA-256=" prefix
            var digestBytes = Convert.FromBase64String(digest);

            using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                store.Open(OpenFlags.ReadOnly);
                var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false);
                var certificate = certificates[0];
                var privateKey = certificate.GetRSAPrivateKey();
                var signatureBytes = privateKey.SignData(Encoding.UTF8.GetBytes(signingString.ToString()), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                var signature = Convert.ToBase64String(signatureBytes);
                store.Close();

                // Construct the "Signature" header
                var signatureHeader = new StringBuilder();
                signatureHeader.Append($"keyId=\"{KeyId}\", ");
                signatureHeader.Append($"algorithm=\"{Algorithm}\", ");
                signatureHeader.Append($"headers=\"{string.Join(" ", Headers)}\", ");
                signatureHeader.Append($"signature=\"{signature}\"");

                return new OkObjectResult(signatureHeader.ToString());
            }
        }
    }

For further Information on this refer to MSDoc1 and MSDoc2.

Rajesh Mopati
  • 1,329
  • 1
  • 2
  • 7
  • Thank you! I've tried this locally, and it works like a charm, but I'm not sure how to make the proper configurations when running it in Azure. I've added the certificate as a .cer in the "Certificates" tab in the Azure Function, but I get a 500 error when I'm running it. What am I missing here? – NicoleB Mar 28 '23 at 11:38
  • To use a certificate in an Azure Function, you need to upload the certificate to the function app and then load it in your code. Upload the certificate to the function app. Go to the function app in the Azure portal. Click on "Platform features" and then "SSL certificates". Click on "Upload Certificate" and upload your certificate in .pfx or .cer format. – Rajesh Mopati Mar 28 '23 at 11:47
  • Load the certificate in your code Make sure to replace "thumbprint" with the actual thumbprint of your certificate. Also, make sure that the certificate is uploaded to the function app and that the function app has permission to access the certificate. – Rajesh Mopati Mar 28 '23 at 11:47
  • I've added the certificate under "Certificates" in the Function App, and it is uploaded as a .cer. When running the function in the portal it seems as the function can reach the certificate, but when trying to initiate the privateKey variable, the privateKey turns out null. Is it necessary to upload the certificate as a .pfx? – NicoleB Mar 29 '23 at 06:19