Introduction

Create and validate secure "License Keys" for your proprietary code and embed up to 16-bits of "configuration data" into the key. This code is flexible and may be used in many different licensing schemes.

Background

Everyone is familiar with getting license keys to activate software. These keys are normally based on various encryption schemes, and serve to validate that a particular user is authorized to install or run the software. The code presented here provides an easy way to integrate this functionality into your own programs.

The keys generated by the sample application are MD5 hashes of a "Licensee" name, a "Serial Number, and a "secret" program name string that is embedded into the code. We then convert the hexadecimal 32-character string to Base32 to shorten the resulting key down to 26 characters. This is easier for end-users to type and looks better and more professional as well.

The code is pretty straight-forward for the most part, and can be easily translated into other languages such as C, C++, C#, Java, etc. and is presented in as "generic" of a Visual Basic form as possible to permit easy integration into applications. It can even be used in VBA applications such as Microsoft Access if desired.

Please note that all code in this article is licensed under the LGPL, so it can be incorporated into your programs with no royalties and doesn't modify the licensing terms of your proprietary code in any way. We do ask that if you make any changes to the key generation code itself that you release the code under the same terms as you received it.

Additionally, this code provides some useful string functions to encode/decode binary values encoded in a string to Base32 and to bitwise left and right shift these values by an arbitrary number of bits.

Using the Code

Simply include the KeyCodes.bas, StrFuncs.bas, and MD5_Crypt.bas files into your project. You can prompt for whatever information you consider to be relevant to your licensing scheme, and it should end up in two string values and one LONG integer value indicating the capabilities you wish to embed into the key code.

It is expected that users of this code will modify the key generation to meet their needs. The keycodes.bas file routines are easily modified to provide different key values and can form an easy base to build your own key code routines. One change that comes to mind is to shorten the Base32 string from 26-characters to 25-characters and then grouping the "digits" in groups of five to provide nicer-looking keys.

Key Strength

The keys generated by this code are relatively secure, as they are based around an MD5 hash of the data used to generate the key. You can improve security by generating a GUID during the installation, and concatenating it with the "user name" field, and then generating the keys via an online submission process. This is left as an exercise to the reader.

Key tampering is highly discouraged by the code's design. Each key is generated from an MD5 hash of the licensee, serial, and your "secret". Once this hash is created, we XOR the "permissions" bits with the last two characters of the key, then drop the first two characters of the key, run another MD5 hash of the truncated key with permissions, and then use the first and last bytes of the second MD5 hash as the first two characters of the final binary key value. These bytes verify the integrity of the permissions bits.

It is also possible to use an algorithm on the final keys to "scramble" them. Just ensure to "unscramble" them prior to attempts to verify the key with what it should be.

Sample Screenshots

Create a key...

Entering a key to check...

A successful check...

A check that failed due to an attempt to modify key "permission" byte...

I wanted this functionality from SQL Server - so I decided to try and convert the VB to VBScript.

I had problems with data types as VB Script only has the Variant data type, but after some work I know have it working and you can just pass the parameters to the VBS script and it will output the desired licence code.

Here is an example usage from the command line:

C:\>cscript getlicencecode.vbs richard.briggs@leansoftware.net EDT

I can't see how to attach a file here so here is the VBScript (Copy and save as getlicencecode.vbs file)

When I convert solution to VS2005, and I try to run it, it always says "Arithmetic operation resulted in an overflow." in MD5_crypt.vb class in function "LongOverflowAdd4" on second line...
Anyone can help me? Please

I am integrating this code example in to a project and found that the sample apps included with the project produce different keys if the target system is set to Japanese (the language of the user who discovered this).

This means that keys generated by my English locale machine do not work if the IsKeyValid procedure within the application is run on a Japanese machine. I am a 'scratch the surface' VB developer and have not been able to isolate which module(s) and procedure(s) are causing this issue but I assume it is something to do with Unicode.

I have been able to replicate the behavior by setting my WinXP machine up as follows:

I haven't reviewed the code sample for this project, but I have had this exact problem with my own product key encoder/validator for the Chinese Taiwan language set.

However, the answer was as simple as changing the text encoding type to ASCII for all my file write and read routines, and changing the default language sets to English (en-us).

For our software requiring a code key, we have the user input it, we validate it, and if passes we write it to registration file which program checks for when first loaded to run in demo mode or full mode.

Code excerpts for VB2008e below:

{Place at top of code before first public class}Imports System
Imports System.Globalization
Imports System.Security.Permissions
Imports System.Threading
Imports System.Resources
Imports System.IO

{Place at beginning of main code class}
' Changes the CurrentCulture of the current thread to en-US. Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US", False)
Thread.CurrentThread.CurrentUICulture = New CultureInfo("en-US", False)

{Place as necessary to restrict the Product Key writing to pure ASCII}
Dim myProductKey As New StreamWriter(myFilePath, False, System.Text.Encoding.ASCII)

{Place as necessary to restrict the Product Key reading to pure ASCII}
Dim myProductKey As New StreamReader(myFilePath, System.Text.Encoding.ASCII)
........^--your own variable........................^--complete path/filename to product key file

Thanks Richard. The idea about converting strings to ASCII prior to running them through the KeyGen module is a good one and will be useful for a new issue I've discovered when running this KeyGen code on a PC and then on a Mac in VBA. I think code pages are causing it to ge messed up

Back to the original 'Windows only' issue, I'm not sure that your suggestion will help since the user names in the example above are already in pur ASCII and there are no file IO operations on the string during the conversion to a key.

This project is very powerful since it allows VBA (not VB) developers to attach licensing modules to code in order to create cross-platform compatible application for Office on the PC and Mac but these locale issues are causing a few headaches.

I't would be great to work with the original developer to try to make it more robust.

The issue is DBCS (double byte character set) systems. Where the source code is using Asc() and Chr(), these can be replaced with AscW() and ChrW(). This should generate the same license key codes whatever the Windows regional settings are for "Language settings for non-Unicode programs".

Just do a careful -- because some variables are using "Chr" string in the name -- text find & replace for these functions and the code should work fine.

I'm using a slightly modified version of the code, but I already encountered this issue and fixed it using this approach.

Hope this helps anyone who wants to use the source code in an international setting.

We've made the [careful] search & replace change from Asc() to AscW() and Chr() to ChrW() in the three modules and checked it on a Windows 7 machine with the locale set to Japan (where it broke before) and it's now working as expected.

After conversion you'll get errors to almost all CShort() functions. But don't worry, you can just take all CShort functions out.
Now the program works brilliantly!

For converting it to VS2008 I can't give you any advise, but I understand this conversion should not be that difficult.

I also tried to convert the program to C# by using one of the free conversion sites you find in Google. Unfortunately that has proven to be quite difficult and took me quite a few hours. It's probably easier to create a encode/decode dll in VB.Net and import it into your C# project.