Microsoft

The Azure Vault, PGP, and other matters Part 2

Ok, where were we (Back to Part 1) … Ah, we are down to the PGP content in the Azure Vault getting ready to use it….

At this point I gather you have created your vault and placed the middle contents of the PGP Key file into the secrets value…. Drilling into my Secret:

We are going to use parts of this detail to pass along to the Get Keys Azure function – as a reminder we are here:

My Azure function takes three values:

SecretName, SecretVersion, and VaultPath. These values come from the URL that you find on the Secret Detail shown above.

https://<VaultPath>.vault.azure.net/secrets/<SecretName>/<SecretVersion>

From the Overview of your Vault, under Settings you will find the Secrets link to get down into the detail window. You will need to click through some windows before you get to the Secret Identifier (the url to the secret). Keep in mind if you added addition key values you get a new Version id each time.

So why do the GetKeys Azure Function this way? Easy, it’s generic. One can place as many PGP keys as needed in either the same vault or same secret and use just different versions of the secret – Or – create a new secret within the same vault – Or create different vaults (Keep in mind on that last choice, one must grant access to new vaults for this or other Azure Functions to be able to access it). What level of “Generic” is up to you, just know the access path.

Let’s get to the interesting part, an Azure Function.

public static class GetKeys
    {
        [FunctionName("GetKeys")]
        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 nameVault = req.Query["VaultPath"];
            string nameSecret = req.Query["SecretName"];
            string versionSecret = req.Query["SecretVersion"];

We start the Azure Function (using VS 2019) by naming it and setting up some Query vars on the Request. This Function is based on Dot Net Core 2.2.0 API. This is how the VaultPath, SecretName, and SecretVersion values come into the function.

On the Logic App side, this the the Queries block:

{
  "SecretName": "<Get From the URL>",
  "SecretVersion": "<Get From the URL>",
  "VaultPath": "<Get From the URL>.vault.azure.net"
}

This next spot of code takes these values and forms our url that is need to speak to the Vault:

string secretKeyUrl = $"https://" + nameVault + $"/secrets/" + nameSecret + @"/" + versionSecret;

To access the vault we need to get a provider token and setup a vault client:

try
{
    var azureServiceTokenProvider = new AzureServiceTokenProvider();
    var kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
    var keyValue = await kvClient.GetSecretAsync(secretKeyUrl).ConfigureAwait(false);

The last statement here is where the async call is made into the value. Note my secret url is used here.

If everything is happy in vault land, keyValue here will contain the secret (middle part of the PGP key that we pasted into the vault value). So we are good, right? Well almost. Remember that part I was going on about in Part 1? The PGP Key format? We have to fix that up now before passing it back to the Logic App.

string privKey = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + "Version: GnuPG v1.4.9 (MingW32)\n\n";

//must fix up the key to look like a real PGP key file
// So why?
//  The format of the key must be
//
// -----BEGIN PGP PRIVATE KEY BLOCK----- [CRLF]
// Version: GnuPG v1.4.9(MingW32)[CRLF]
// [CRLF]
//   <key detail> [CRLF]
//   <key detail2> [CRLF]
//   <key detail3> [CRLF]
// [CRLF]
// -----END PGP PRIVATE KEY BLOCK-----
//
//  The "vault" does not store the [CRLF] Gotta love that
//
//
privKey = privKey + keyValue.Value.Replace(" ", "\n") + "\n-----END PGP PRIVATE KEY BLOCK-----";

The comment section shows what we are trying to do here… take the value from the vault surround it with the PGP header/Trailer while putting back in the [CRLF]’s

Yes, I agree this is a colossal load of crap, but this is what we have.

The last part of the GetKeys function:

            return new OkObjectResult($"{privKey}");
        }
        catch (Exception ex)
        {
            log.LogInformation($"Error getting key from Vault: {keyUrl} Message: {ex.Message}");
            return (new BadRequestObjectResult($"GetKeys Error {ex.Message}"));

        }
    }
}

Returns back the Ok (200) with the response body containing the PGP key content.

In the end, it’s not too bad, and it does work…

In last part, we will finish up by decrypting some stuff…. See in Part 3 (or go back to Part 1).

~SG

Leave a Reply