You’re going to create license keys for your product and you figure the hard part is the license checks. No. The part that will bite you in the ass is not having necessary properties in the key. You can have the greatest license check code in the world and if you don’t have properties you want to check against – it limits your options. So let’s talk properties.
BTW – many of these are lessons we learned the hard way. Our license key was first created for a single product (the Windward engine) which ran on only on Java, and only accepted XML for data. Extending that to handle multiple products, multiple operating environments, multiple datasources, etc. significantly expanded the properties we need.
A license should be able to hold multiple keys. So we will break this out into license properties and key properties. The purpose of multiple keys is you can deliver a single key when someone purchases a combined product.
- A unique ID. This is useful even if you don’t have the software phone home on use to see if the license has been revoked. When a key is revoked, add that ID in your program and have the license check fail if the license used uses that ID. This limits a revoked license from being used in any service packs or updates released after the revocation.
- An expiration date (Date.Max means no expiration). All demo keys have an expiration date. All keys delivered when you receive a P.O. have an expiration date of 2 weeks after the payment due date. The few times a company has delayed paying a P.O. and hits the expiration, we have someone on the phone frantically trying to give us a credit card number because their key expired and their system won’t run.
- A maximum version number, that allows both forms “22.214.171.124” and “1.2.*.*”. If someone purchases version 3 from you a license max version of “3.*.*.*” means they cannot use it on version 4. And don’t assume the numbers will be a single digit!
- A minimum version number, in the same form. Why a minimum version? We’re adding per/report licensing. Without the minimum version we have to create new product names (see key properties) because the key otherwise could be used on older versions for unlimited free reports. With a minimum version we could have just added the new properties and only allowed the license on newer versions that use the new properties.
- The company name. The name of the company the key is issued to.
- The license description. This is normally null but can optionally populated for cases where a company has multiple keys and needs descriptions in each for their own use.
- A boolean for every license check performed. If this is set to false, that check is not performed for the license. This is rarely needed, but when it is needed, it is needed immediately.
- Hosts the software can run on. Again rarely used but if you have a customer who you distrust, you ask for the names of the computers the software will be used on and enter them here in the form “sheldon;leonard;howard;raj;penny” and only allow the system to run on a computer with that name (make sure you strip off the domain part of the name you get from the O/S).
- Additional properties you need to use against all keys. We have one for IsDemo that returns true if the key is a demo license.
- Product as an enum or int. You are starting with one product but you will have more. You can default this not being specified to be the first product but your code is then more complex and better always have this special case. So get this in from the start.
- Platform as an enum or int. You may want to break this into two parts (for example Java, Android). We’ve done fine so far with Java & .net but we may shortly wish we also had a sub-platform (Android, Windows tablet, etc.).
- NumberMachines. The number of machines the license is good for. When a corporate customer buys licenses for 12,000 users, trust me, they do not want 12,000 different license keys.
- NumberCores, NumberThreads, NumberUsers, NumberReports, etc. If your license is limited by any system property or usage level, you need those limits in the key. We used to price our server by the number of cores in the server. With the proliferation of servers with humongous numbers of cores, we now also limit by the number of concurrent threads that can be running our system.
- Enabling subsets of the program based on the key. We change the menu (limiting what functionality is available) based on the key for our desktop applications. So we have multiple “applications” where they are the same program, they just have a different key. Whenever possible, don’t have several modes, have bit flags that turn functionality on/off and for those modes set the flags appropriately. That way, when you want additional modes, it’s just a different setting for the flags.
You need to determine what counts as a distinct use of the key. Is it unlimited use on a physical computer, across multiple VMs? Is it one VM but across multiple JVMs/CLRs? Is it a single JVM/CLR? And for a single use, does it allow multiple users when they have a Citrix server? For our desktop apps we get the “machine name:current user name” and that string counts as a distinct user. We implemented this after we discovered some customers buying a single copy, placing it on a Citrix server, and then having 80+ people use it.
Never ever change the meaning of any property. We changed the meaning of NumberCores == 0 to mean unlimited – and then any earlier version could not run with that key. And by the time we realized the mistake, we had versions out depending on that behavior. We should have added IsCheckNumberCores and left NumberCores alone.
We create our license keys as an XML file that is first encrypted and then uuencoded. The XML makes it easy to add new properties and the older version can use the new keys. The <license> node has 1..N <key> child nodes. The encryption keeps the key safe. The uuencoding makes it easy to email the key to people.
If you start with the above properties, you’ll find it easy to expand your license keys while remaining backwards compatible. I wish we had started with all of the above.