Rhino Licensing

Rhino Licensing is an open-source licensing framework by Ayende Rahien, and it grew out of his frustration with other license providers while creating NHibernate Profiler.

Rhino Licensing uses asymmetric encryption–also known as public-key cryptography–specifically the RSA algorithm. In cryptography, asymmetric means there are two parts to each key, one public and one private. You encrypt a value using the one key, and it can only be decrypted using the other key. In the case of license key generation, we store our private key on the server (and never tell anyone it!), and distribute the public key with our application. When the user receives a license key, the application is able to verify that it came from us by using the public key. If someone tries to use a license key they’ve generated themselves, it wouldn’t work unless they had our exact private key.

Other systems that use asymmetric encryption include SSH, SSL, PGP, and among others, Git uses asymmetric encryption for its security due to it using SSH.

The format of the license that Rhino Licensing generates is an XML document, plain and simple. By default, it includes the user’s name, an expiration date, the type of license (trial, standard, floating, etc…), and most importantly the cryptographic signature. Below is an example Rhino Licensing generated license.

<?xml version="1.0" encoding="utf-8"?>
<license id="ae4c05b5-c188-47f8-852f-b4e5375621f7"
    expiration="2011-01-01T00:00:00.0000000" type="Standard">
  <name>Bilbo</name>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />

      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>sKDT7gzgedzmh1AMxxLbfcF1Hsw=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>
        SfkvCGv1+EdLTvaROv27ymDumS0y02fPANTVhr0Yxd/
        AxxVH0q0BQ6w8Ou5L7gyLYLvnSckgjhrGnGpiifdvbg==
    </SignatureValue>
  </Signature>
</license>

What’s a signature?

A cryptographic signature is a string of bytes used to aid in verification of the source of a document. In most cases–and in the case of Rhino Licensing–the signature is generated by hashing the content of the file using SHA1 (or another suitable hashing algorithm), and encrypting that hash using the private key. The resulting string of bytes is the signature.

When the client receives a file with a signature, it creates a hash of the file (sans-signature) and compares that hash to the decrypted value of the signature. Baring in mind that the signature is an encrypted hash of the file, if either of the public or private keys were invalid or the file had been tampered with, then the decrypted string would not match the hash of the file. When the client generated hash and the decrypted signature match, you can be confident the sender of the message is who you think it is.

The great part about using the hash of the document as the signature is that if the user tampers with the license–such as changing the expiration date or the licensee name–the signature will no longer match the hash the client generates, and BAM, instant invalid license.

One particular aspect I like with this approach is that the licensee name is clearly visible in the license. This simple act can be quite dissuasive when it comes to piracy; after all, who would want their name visible on all the torrent websites out there?

Enough with the waffle, let’s start using Rhino Licensing.

Create your key

The first thing we need to do is generate a public/private key pair for us to use in our license generation. Rhino Licensing uses the RSACryptoServiceProvider for it’s keys, so we can use that ourselves to generate our keys. One of the simplest ways to do that is to knock up a really quick console application.

class Program
{
  static void Main(string[] args)
  {
    var rsa = new RSACryptoServiceProvider(1024);

    File.WriteAllText("publicKey.xml", rsa.ToXmlString(false));
    File.WriteAllText("privateKey.xml", rsa.ToXmlString(true));

    Console.WriteLine("Done");
    Console.ReadKey();
  }
}

That program will generate a new key for you and write out the public and private parts to two separate XML files (publicKey.xml and privateKey.xml). Keep these two files handy!

You will probably want to change the 1024 value in the RSACryptoServiceProvider constructor to something larger. This is the size of the key that will be generated. The larger the better really, but larger numbers require longer to generate.

Update your application

Now we’ll update your application to complain if it doesn’t have a license. In your entry-point for your application you need to assert the license. Before we do that, make sure you’ve got your public key handy from the previous step. Rhino Licensing needs the content of this file, so you can either embed it directly into a constant or as a resource. Secondly, it needs to know where to look for a license that the user will have been given.

Assuming a really simple app, here’s a Program.cs with Rhino Licensing:

class Program
{
  static void Main()
  {
    var publicKey = File.ReadAllText("publicKey.xml");

    new LicenseValidator(publicKey, "license.xml")
      .AssertValidLicense();

    Console.WriteLine("Hello");
    Console.ReadKey();
  }
}

If the license.xml file doesn’t exist, or if it’s invalid, Rhino Licensing will throw an exception. Of course, as we haven’t generated a license yet we’ll get an exception. That’s the client done though, so we can move on to our final step now. Once we complete that step, you will be able to just drop a license.xml into the same directory as your application and everything will work as expected.

Create the license generator

We need something to actually generate a license for your users. If you were being all fancy you could do this as a web-service that gets integrated with your payment provider; however, for simplicity’s sake we’ll just create another console application that spits out a license.

Rhino Licensing has a few prerequisites for a license: A name, an id of the license, an expiration date, and a license type. You can also supply a dictionary of custom values, but we wont be using that in this example.

For a really simple license generator all you need is the above for each license, and the private key. We’ll wrap that up in a simple console app which’ll prompt for the required info:

class Program
{
  static void Main(string[] args)
  {
    var privateKey = File.ReadAllText("privateKey.xml");

    var id = Guid.NewGuid();
    var generator = new LicenseGenerator(private_key);

    Console.Write("Name: ");
    var name = Console.ReadLine();

    Console.Write("Date: ");
    var expirationDate = DateTime.Parse(Console.ReadLine());

    Console.Write("Type: ");
    LicenseType licenseType = (LicenseType)Enum.Parse(typeof(LicenseType), Console.ReadLine());

    // generate the license
    var license = generator.Generate(name, id, expirationDate, licenseType);

    Console.WriteLine();
    Console.WriteLine(license);
    Console.ReadKey();
  }
}

That’s it, you’ve got a license generator. Remember, this will exist behind your firewall, the users will (and must) never see your private key.

If you take the output of this application and save it in a license.xml file in your client, your application should stop throwing license exceptions and run normally! You’ve officially just licensed your app.

What have we just done?

  • Created a private key generator, purely for convenience than anything else. We should only need to run this once before we start generating our keys. You could run it once per major release to ensure your 1.0 licenses are never compatible with 2.0.
  • Updated our client application to validate the presence and validity of a license (and to explode if there isn’t one).
  • Created a “server” to generate licenses for us. This is very primitive but could easily be adapted to a web server. You might want to look at the Rhino Licensing LicensingService before you continue.