Kerberos.NET is built to be used across multiple platforms, however there are some caveats.

Historically people have tended to think .NET is Windows-only and therefore any library built on it is only going to run on Windows. This hasn't been the reality for a few years now and .NET will run mostly anywhere these days. Kerberos.NET will run on any supported .NET Standard 2.0 runtime, which today is Windows, Linux, and Mac OS. The recommended method is using the nuget package.

PM> Install-Package Kerberos.NET

However, there are some important caveats.

Cryptographic Support

The cryptographic primitives used by the Kerberos protocols are available in .NET across all platforms, however there are extensions to the protocol that rely on primitives that are not exposed at the framework level. There are two extensions that do not work for time being:

  1. RFC 4757: RC4-HMAC -- This would be better described as the MD4/RC4/HMACMD5 suite. It's the defacto suite introduced in Windows 2000 for compatibility reasons and has since been deprecated by the standards bodies. The MD4 hash is not exposed through .NET (for good reason) and must be P/Invoke'd or written in managed code. It's not a particularly complicated hashing algorithm, but it's also not worth taking on for the sake of cross-platform support. You're not affected in any way if you're using AES (as you should be).
  2. RFC 4556: PKINIT -- This relies on a Diffie-Hellman key agreement, which is also not exposed through .NET (also for good reason). This is somewhat more problematic because it's an important extension and should be supported everywhere. It's not a particularly complicated algorithm and in fact there's a poorly written implemention in the test harness. This needs to be better before it would be considered for library support. This might also be moot once Elliptic Curve support is added.

Adding Your own Cryptographic Implementation

If you're so inclined and need either of the above two extensions you can bring your own implementation by registering your own platform layer:

CryptoPal.RegisterPal(() => new MyCustomCryptoPalImplementation());

It needs to be called before any crypto calls occur, otherwise you're going to defaulted to the existing platform implementation.

DNS Resolution Support

Kerberos relies on DNS for resolving KDC locations by checking for SRV records of the Kerberos service at _kerberos.tcp.realm.net (or UDP). This is necessary if you don't know the location of the KDC you're needing to authenticate against. Unfortunately .NET doesn't expose a way to query SRV records, and the library relies on P/Invoke into the Windows DnsQuery_W function to do this. You can bring your own DNS query logic by overriding the QueryDomain method on the client transport classes TcpKerberosTransport, UdpKerberosTransport, and HttpsKerberosTransport.

Unit Tests

The unit tests are inherently Windows-centric and P/Invoke for a myriad of interop and compatibility reasons. This isn't a problem for developers just grabbing the nuget package, but it is a bit problematic for anyone wanting to extend the library or fix the above limitations on non-Windows platforms. Eventually the tests may be reorganized and tagged as platform-specific, but that's not a high priority at the moment.

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.

The next logical step for the Kerberos.NET project is setting up automated builds and releases. What exactly does this entail? Basically, I want a build to kick off any time changes are committed to the main repo, and automatically generate a production-ready Nuget package that is available to upload if deemed worthy of release.

If you’ve done any sort of build automation or release management before, you’ve got a pretty good idea of how to make this work. For a given build service do the following:

  1. Observe changes to repo
  2. Pull down changes
  3. Build project(s)
  4. Package the packages
  5. Artifact the necessary files for future release

And if you’re really wanting to be fancy:

  1. Push packages to nuget repository on release signal

I chose to use Visual Studio Online to do this for me. I can use hosted build agents to build and package the project without a ton of effort. This is how I did it.

First, you need to have a Visual Studio Online account. If you don’t have one, go set one up. It’s easy.

Next, you need to create a new Team Project. Give it a name, select the Version Control and Work Item types. You can leave these as the defaults unless you have specific reasons to use the other options. The options don’t matter at all if you’re using Github because you’ll never commit or track work here.

Now you need to wire it into Github to track changes. Select the gear icon at the top to get into the project settings and select the Services tab.

Select the Services Tab

Add a new Service Endpoint and select Github:

Select Github

Now you can configure which account to connect to Github as. Just select the Authorize button and you’ll see a popup asking to be… authorized. There are more advanced options to configure if you’re specifying things like personal access tokens too. Your browser may block the Authorize pop up window. You’ll need to allow the pop up before it’ll allow you to continue.

Authorize Github

Now you can start configuring the build. Select the Builds and Releases tab and click the New button.

Add a New Build

And now you need to select a build template. We’re now building a .NET Core project in the case of Kerberos.NET, so you can start with an empty process or select the ASP.NET Core template.

Select your Build Template

Next you’ll want to configure the Get sources step, which will checkout changes to the repo. You need to select the Github tab. It’ll automatically populate the fields because you previously registered Github as a service. Select which repository you want to use and configure branches or other settings.

Configure Get Sources

Next you’ll need to configure the Restore/Build/Test/Publish steps. By default these steps are configured as .NET Core 1.*, which in our case is incorrect. We need to use .NET Core 2.0.

Select .NET Core 2.*

You’ll need to do this for the 4 steps. You can probably leave all the other settings alone and the system will just pick up any projects it needs to build or test — assuming you’ve got tests.

One thing I’d recommend you do is also add the new task before the restore or build called the “.NET Core Tool Installer”.

Add .NET Tools Installer

The reason for this is that it’ll download and install any runtimes or SDKs for any future versions of .NET that haven’t been added to the build agents yet. This isn’t strictly necessary, but it may save you some headaches in the future (or in the past, in my case, because the 2.* versions weren’t available when I originally set this up).

Install .NET Builds

From there you can configure the build/test/publish steps as necessary. Once you think you’re in a good spot go ahead and queue it up:

Save and Queue the build

You’ll be asked which queue to use. You’ll want the Hosted VS2017 agent in this case because of .NET Core 2.0.

Queue the Build

The build should then kick off with and you can track the results based on the console output or the logs. You can see there’s been some, uh, trial-and-error.

Build Build Build.

Once you’ve got a working build you can start configuring how you want it to kick off through the Triggers mechanism.

Trigger on Commit

In this case trigger when there are changes to the master branch. The Pull Requests option is super-handy when you’re working with teams and actually use PRs — there’s only been one PR with Kerberos.NET, and that was before builds were set up. You should consider committing to the project!

Once your build is ready to go you can even badge it to see the most recent build status easily:

Badge the Build

And now you have can embed the status wherever you want:

Next time we can look at automatically publishing.