There’s a common problem that many applications run in to when executing cryptographic operations, and that’s the fact that the keys they use tend to exist within the application itself. This is problematic because there’s no protection of the keys — the keys are recoverable if you get a dump of the application memory, or you’re able to execute arbitrary code within the application. The solution to this problem is relatively straightforward — keep the keys out of the application.

In order for that to be effective you need to also move the crypto operations out of the application too. This means any attacks on the application won’t yield the keys. This is the fundamental design for services like Azure Key Vault, Amazon CloudHSM, or any other HSM service or device. In fact, even Windows subscribes to this design with CNG keys using LSASS.

I was thinking about this problem over the weekend and realized there isn’t really any good reference architecture out there that shows you how to build this design into your application. The services I mentioned earlier do great jobs at protecting secrets, but they’re kind of designed with certain applications in mind — greenfield, cloud first, or disconnected. This can make it difficult to migrate existing applications to use these services, or maybe you just can’t use them for whatever reason (regulatory, legal, etc.). On top of all that, you don’t even know how to start.

So, I did something dumb. I built a reference implementation that lets you move crypto operations out of your application and into a separate process.

Introducing: Enclave.NET.

READ THIS SECURITY WARNING

This is a reference implementation. That means it’s not designed to handle production loads, and absolutely is not built to withstand attack. It’s a sample intended to show how you might offload certain operations. There are probably some horrible bugs in here and there might even be vulnerabilities.

Was that sufficiently scary enough?

The basic idea is simple. You have an application that hosts a service. The service has a set of commands available to it:

  • Generate key
  • Encrypt
  • Decrypt
  • Sign
  • Validate

Your application calls these commands with the necessary payloads, the service does the thing, and returns a result. The service is HTTP-based, and protected with pinned client certificates.

The crypto operations are real, backed by the jose-jwt library, but they’re ephemeral — the keys are just stored in memory. The idea is that you can inject your own implementations as you see fit, so any migrations you might undergo can be gradual and painless.

There are two classes that need to be implemented — the crypto operations class, and the storage service. You can use the built-in crypto operations class InMemoryCryptoProcessor at your own risk, but you absolutely need to implement the storage, lest you lose all the keys when the app shuts down.

Crypto Processor

Storage Service

You can modify the startup code on your own, or you can implement IStartupTransform and configure it:

Calling the service is simple:

For more information take a look at the sample app.

Earlier we looked at how to build and package and then deploy nuget packages. One thing (of many) I glossed over was that whole version thing. It turns out versioning is really difficult to do. It’s kind of like naming things.

I’m not going to go into the virtues of one method (like semantic versioning) over others, but really just going to show how I set it up so my silly little project always has an incrementing version number after build.

First, I created two new build variables “MajorVersion” and “MinorVersion”, setting their values to match the current static version.

Set the Variables

Once the variables were set I could use them to set the BuildNumber variable in the build options.

Set the Build Number

This uses the major and minor as the static values created earlier, the current month/day for the build, and the revision number for the build revision. This’ll generate a value along the lines of 1.5.0819.1 and will increment to 1.5.0819.2 until tomorrow, which’ll bump up to 1.5.0820.1, etc. Yes, I’m a monster.

With the build number defined we can configure the build task to explicitly set the Version property with -p:Version=$(Build.BuildNumber).

Set Build Version

Now any new builds will use this version format. This way I don’t have to worry about forgetting to increment the build numbers on small fixes and releases. I still have to increment the major and minor versions when anything big is released, but that’s reasonable and expected.

Earlier we looked at the automated building and packaging of Kerberos.NET using VS Online. At this point the only thing we get out of it in this state is knowing the code compiles and any tests pass — which I suppose is actually quite a feat on it’s own considering I had no indicator of either when I started. We can, of course, do better! The last piece we need is automated publishing of the final nuget package to the feed for others to consume.

As it turns out this is relatively straightforward to do. The process is:

  1. Defining artifacts to keep after build succeeds
  2. Create a On-Release task that uploads the package to nuget

Defining the build artifacts is easy. All you need to do is specify that the output of the build should go to the staging directory, which is the variable $(Build.ArtifactStagingDirectory). You can also explicitly set the Version here too, if you happen to be using the build system to increment build versions.

Set Build Output Directory

Now all you need to do is publish the collected artifacts to nuget. To do that you first need to configure the nuget service, similar to how you configured Github. First go to the settings via the gear icon and select Services. Add a new service and find the nuget option. This’ll prompt for connection details:

Adding the Nuget Service

The Feed URL for nuget.org is https://api.nuget.org/v3/index.json. The ApiKey can be found in your Nuget account API Keys. If you don’t have an API Key, go ahead and create one, setting the expiration to 365 days. Oh, and be sure to set a reminder for 365 days from now to tell you the key needs regenerating. *grumble*

Lastly verify the connection and hit Ok. Once your service is configured you can go back to your build and select the Releases tab. Hit the + icon to add a new release definition. Create an empty template and give the environment a name. Click into the environment and add a new task to the Agent Phase. Add the Nuget task. Set the command to push, configure the path to the packages. In this case we used $(System.DefaultWorkingDirectory)/Kerberos.NET CI/drop/*.nupkg. Then of course, select the Nuget service you created earlier.

Configure the Task for Push

Now that we have a release task, we can kick off a new build, or just work with an existing build that has the artifacts. At the top of the build details screen is a Release button. This brings up the release dialog that lets us kick off the process.

Kick off the Release

Go ahead and kick off the release. It’ll take a few seconds to kick off, but once it’s done and if all things went well you should see the new package available in nuget (yes, the version numbers are different — I don’t want to kick off a new package without any changes).

Nuget Shows our Package!

Now I don’t have to worry about manually futzing with generating and upload package files — if I’m happy with the given build all I need to do is tell it to release.