I started the Kerberos.NET project with a simple intention: be able to securely parse Kerberos tickets for user authentication without requiring an Active Directory infrastructure. This had been relatively successful so far, but one major milestone that I hadn’t hit yet was making sure it worked with .NET Core. It now works with .NET Core.

Porting a Project

There is no automated way to port a project to .NET Core. This is because it’s a fundamentally different way of doing things in .NET and things are bound to break (I’m sure that’s not actually the reason). There is documentation available, but it’s somewhat high-level and not necessarily useful when you’re in the weeds tracking down weird dependency issues. Given that, you’re kinda stuck just copying over the code and doing the build-break-tweak-build-wtf dance until you have something working. This was my process.

Create a new .NET Standard project — Standard dictates the API level the library is compatible with; Core dictates the runtime compatibility. I didn’t have any particular targeting requirements, so I made it as broad as possible. Now any runtime supporting .NET Standard can use this library.

Copy all the code from the main project into the new project — I probably could have created the .NET Standard project in the same location, but it’s often easier to start with a blank slate and move things in.

Build!

Build fails — MemoryCache/ObjectCache don’t exist in the current API set. Thankfully this is isolated to just the Ticket Replay detection, so I was able to temporarily convert it to a simple dictionary. I eventually found a library that replaces the original caching library.

Build fails again — SecurityIdentifier doesn’t exist in the current API set either. Doh! I wasn’t going to hold my breath waiting for this to be moved over, so I created my own class that had the same usefulness. This also gave me the opportunity to fix some ugly code required to merge RIDs into SIDs, which added a nice little performance boost.

Build succeeds!

Unload/remove the original .NET 4.6 projects from the solution.

Adjust test project dependencies to use the new project instead.

Run!

Once I was able to get the test projects up and running I could run them through the test cases and verify everything worked correctly. Everything worked, except for the AES support. 😒

Porting a Project with a Dependency

I added support for AES tickets a while back and it was built in such a way that it lived in a separate package because it had an external dependency on BouncyCastle. I’m not a fan of core packages taking dependencies on third parties, so I built it as an add-on that you could wire-in later. This worked relatively well, until I needed to migrate to .NET Core.

It turns out there are a number of Core/Standard/PCL packages for BouncyCastle, but what’s the right one? Weeeeelll, none of them, of course!

At this point I decided to suck it up and figure out how to make SHA1 do what I want. One option was to muck with the internals of the SHA1Managed class with reflection, but that turned out to be a bad idea because the original developers went out of their way to make it difficult to get access to intermediate values (there are philosophical arguments here. I don’t fault them for it, but it’s really frustrating). I considered rewriting the class based on the reference source, but that too was problematic for the same basic reason. Eventually I ended up porting the BouncyCastle implementation because it was relatively self-contained, and already worked the way I needed.

Security note: You should never trust crypto code written by some random person you found on the internet. That said, there’s a higher chance of finding a vulnerability in other parts of the code than with the port of this algorithm, so…

This actually works out well because now all the code can go back into a single package without any dependencies whatsoever. Neat!

Porting a Nuget Package

The nuget pieces didn’t really change much, but now the manifest is defined in the project file itself, and packages are built automatically.

Simpler Package Management

Now the package is just an artifact of the build, which will be useful if/when I ever move this to an automated build process.

4 Comments

Yes, that’s exactly what that means. The session initiation and management is left up to the implementer, but it’s relatively trivial if you base it off the ASP.NET Identity stuff. You also need to account for the key management aspects of this — getting the service principal created in AD, and generating a keytab, etc.

Is there any hard limitation why you chose netstandard 2.0 instead of netstandard 1.3? Is it the Microsoft.Extensions.Caching.Memory dependency? It looks like v1.1.2 of the library supported netstandard 1.3.

If there isn’t a limitation, I would be really interested in having support for netstandard 1.5 or below, I could create an issue on GitHub and start from there, wdyt?

Good question, I don’t know! 🙂 I just went with 2.0 because that was the default. It’s certainly possible it could work with a lower version, but I haven’t tried. I’m not so concerned with the cache dependency. That can be externalized fairly easily. Go ahead and create the GitHub ticket. Bonus if you’d be willing to give it a try and see what breaks? I can take a look in the next day or so, but I can’t guarantee any progress if it breaks a ton of things.

Author Spotlight

Steve Syfuhs is a security software builder. He has spent the last decade building secure systems and is currently working at Microsoft as a Windows Identity Program Manager in OS Security. He was a Microsoft Developer Security MVP between 2011 - 2018.