July 5, 2016

Has Code, Use KeyVault

You write good code, we all know this. Actually, if you are like me, you write really, REALLY bad code, but are fortunate enough to have friends that can point you in the right direction when things get off the tracks. Recently, I had such an exercise as I was needing to rotate API keys in my ASP.Net MVC application hosted out on Azure.

Apparently, you aren't supposed to store said keys in your code, because when they change (often), you have to redeploy your code. That itself is enough to warrant not doing that, but there are other reasons (many in fact). Wouldn't it be nice if all the other developers that can see your code NOT have access to your API keys, connection strings or secrets? Yes, I know, welcome to the 21st century, but as I've often splain'd, I'm NOT a developer! Be that as it may, I needed a better way to store these keys and other secrets and I also did not want to put them into Application Settings for various reasons. Answer? KeyVault!

This is pretty straight forward for you dev heads but it does string a few nice things together for a complete solution. First the problem:

private string APICode = "abc123"



Certainly the above code is easy and what I'm about to do is a lot more difficult, but that's typically beside the point. My resolution is to put APICode into KeyVault.

First, I need a KeyVault. Log in and get your subscription correct, then:

New-AzureRmKeyVault -VaultName "MyKV" -ResourceGroupName "My RG" -Location "East US"

Next, I need a service principal that has rights to read the secrets out of KeyVault. If you are using AAD Auth in your app, like me, you already have one. Else, create one and give it rights to your KeyVault to 'get' secrets. I referenced the great tutorial here. You'll put the ClientID and ClientSecret into your web.config from this service principal that you gave rights to. For me, I chose to do appSettings references in case those need to change later too.
<add key="ClientId" value="dummyvalue" /> <add key="ClientSecret" value="dummyvalue" />

Important: dummyvalue is literally what I put there, it will be replaced at runtime by the corresponding VALUE in App Settings from Azure. You could put literally anything into the Value field in web.config you want, it will be replaced at runtime.

Once you have that, then you are ready to set your APICode into it. Note that KeyVault secrets must be of type "Secure-String."

$ss = ConvertTo-SecureString 'abc123' -AsPlainText -Force

And put it into the Vault:

Set-AzureKeyVaultSecret -VaultName "MyKV" -Name "APICode" -SecretValue $ss

There are other neat things you can do with this like versions, expiration, disable, etc., but we're taking it slow today. Once you run this, it will give you a URI back as the ID. You want to copy this down and looks something like this:


The GUID at the end is the version. If you don't care about versioning (IE, the API code only rolls forward, never backwards, you can leave off the GUID at the end and just grab the rest.

Now, I could stick that into my code as a reference to the actual secret, but I've still got myself a dependency that is stupid. If the KeyVault name ever needs to change for whatever reason, I have a code push requirement. Instead, I'll place this URI into Application Settings and reference THAT in my code and make Azure tie it all together for me.

First, I open up Azure Application Settings and create an entry for it and place the URI into it:


Next, to refer to that in my app, I simply reference it in my web.config in the section:

<add key="APICode" value="dummyvalue"/>

Now, there are a few dependencies I need to take care of before I change out my code. First, I need to reference a package from NuGet to make my app know how to talk to KeyVault.


Now, I only want to call into KeyVault when my AppPool spins up and then keep that value in memory. To do that, I simply reference KeyVault in my Global.asax.cs file and instantiate it:


and wire it up with this line after default things are finished:

var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(Utils.EncryptionHelper.GetToken));

If you just copy/paste that in, you'll notice it's missing Utils.EncryptionHelper.GetToken. That's cause we're going to create a helper class that I shamelessly borrowed from Azure Documentation, pasted here in its entirety:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.IdentityModel.Clients.ActiveDirectory; using System.Web.Configuration; using System.Threading.Tasks; using System.Diagnostics;

namespace AzureStatus2.Utils { public class EncryptionHelper { public static string SecretAPICodeValue { get; set; } } public async static Task<string> GetToken(string authority, string resource, string scope) { var authContext = new AuthenticationContext(authority); ClientCredential clientCred = new ClientCredential(WebConfigurationManager.AppSettings["ClientId"], WebConfigurationManager.AppSettings["ClientSecret"]); AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null) throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken; } } }

Couple of things to note about this little helper class:

  1. See where it references WebConfigurationManager.AppSettings["ClientId"]? That's where we grab ClientID and then Secret from AppSettings in Azure. This is the service principal you created (or reused) above and granted 'get' permissions on the KeyVault. This is you 'logging in' to Azure KeyVault.
  2. Note that I am using public static string in this class to hold the secret 'abc123' API code. The static is important so that the system knows it needs to hold this thing in memory during the entire execution of the app. Since it is IIS, that means, until its next restart. This way, I don't have to keep going after it in the KV which, while fast, would add unnecessary page load or processing time.

Now, I need to actually set that value. Back over in global.asax.cs, which is basically the 'onStartup' spot for my app, I can tell AKV to get the secret value now that I've instantiated the KV object and stick it into that variable in my EncryptionHelper class:

Utils.EncryptionHelper.SecretAPICodeValue= kv.GetSecretAsync(WebConfigurationManager.AppSettings["APICode"]).Result.Value;

Two important things about this line:

  1. I'm setting that static value in Encryption Helper with the Result's VALUE (the actual string 'abc123').
  2. The URI of my secret is the parameter needed of the GetSecretAsync() function, which is housed in my AppSettings of Azure, which I refer to in web.config.

Now, I have written all the code I need to get that super secret APICode out of my code. So how do I reference it? Back to my original problem code:

private string APICode = "abc123"



is now simply:


May look like a lot to accomplish a little, but that little is worth its weight in gold now that I know how to wire all this up securely. I can also now reuse this bit of work for all connection strings, other sensitive data, etc in my code and be worry free when I post this up to GitHub for people to laugh at.