Introduction
This article provides an insight on how to generate un-crackable and flexible product keys. Strong product keys means less piracy and less lost revenue for a software publisher.
Background
License management is part of almost every commercially available product. The financial success of a commercial software solution depends heavily
on correct licensing management. Finding ways to enforce licenses on software has never been easy. Software pirates always find creative ways to bypass
licensing mechanisms, partly because the current hardware was not designed from the ground up with rights management in mind, partly because developers
do not invest much time and resources into license management, some resulting solutions ending up easy to crack and bypass, thus causing loss of money.
Part of license management consists of managing license keys. License keys are pieces of data used by software products to verify the fact that a particular
customer has been granted a license to use a particular software product (by paying to use the product, for example).
Securing product keys
When generating a product key, one of the biggest challenges is to ensure that the key was generated by the product publisher and not by an unwanted party
like a pirated key generator. In offline scenarios (when the key cannot be validated over the Internet against a database of generated keys), the key must somehow
carry within itself the proof that it is "authentic". An approach is to layout the key binary data like this:
KEY = KEY_DATA | ENCRYPTED(HASH(KEY_DATA))
In order to validate the key, the key data is hashed, and the encrypted hash is decrypted. If the computed data hash is identical to the decrypted data hash,
it means that the key used to decrypt the data is the correct decryption key. Now this is where it starts to get interesting.
Using symmetrical encryption to secure product keys
Many software authors resort to using a symmetrical encryption algorithm for generating product keys, and validating the product keys using an encryption/decryption key
that is "hidden" within their product. This means that once a malicious party obtains the symmetric encryption key by reverse engineering the software, they are able
to create key generators. Aside from the financial losses due to piracy, this also leads to big logistical problems for the software publisher. It usually means changing
the symmetrical encryption key and releasing a new version of the product which only works with the newer generated keys. Which in turn means many support calls from customers
trying to enter a newly purchased product key into an old version of the product, etc. It's worth mentioning that not all the symmetrical encryption algorithms
are suitable to use in encrypting product keys, because some of them produce large encryption data sizes, which leads to unusable large product keys.
AES, for example, is not a good approach because the block size has 128-256 bits so it's too large. Blowfish is a good algorithm for this because the block size is 64 bits.
Using public key cryptography to secure product keys
A much better approach for ensuring the authenticity of the product keys is to use public key cryptography, like the well-known RSA. This uses different keys
for encryption and decryption. In this approach, HASH(KEY_DATA) is encrypted using a secret key and concatenated with the key data. To validate the key,
the ENRYPTED(HASH(KEY_DATA)) is decrypted using a public key embedded with the product. If the decrypted data matches the key's data, it means that the key
was encrypted ("signed") with the private key which is only held by the product publisher and not hidden in the product. The public key can only be used
for decryption (signature verification), so this means that even if a malicious party knows this key, it cannot generate product keys.
So why doesn't everyone use public key cryptography to secure their license keys? The answer is that in public key cryptography, the digital signatures are very large.
This means that the resulting product keys are very large, and no one would want a license key to be 200+ characters long. However, some developers use license files, instead of product keys.
A license file contains a very large product key, sometimes in binary form, sometimes encoded using Base64 or other encodings. WinRAR is such an example.
When you buy the product, you receive a license file instead of a product key. This approach has severe usability limitations though. You can't spell the product key
over the phone, you can't print it on a sticker or on a DVD, you can't easily give license keys to your resellers, etc.
Introducing Elliptic Curve Cryptography
Elliptic Curve Cryptography (ECC) is an approach to public key cryptography based on the algebraic structure of elliptic curves over finite fields.
It was invented in 1985 by Neal Koblitz and Victor S. Miller. One of the key advantages of ECC over RSA is that the keys are very small and just as cryptographically
strong as the larger RSA keys. When using digital signature algorithms using ECC, the signatures are much smaller compared to the RSA signatures, making this approach
suitable for generating product keys. One other important advantage of ECC is that there are many different valid signatures for the same data and signed
with the same private key. This is of outmost importance, allowing to create many different keys without varying the input data via random values added to the data.
This keeps the license keys small. The disadvantages of this approach are the difficulty of implementation and some patents held by various companies (notably Certicom)
regarding various ECC techniques. However, there are some ECC implementations unencumbered by patents, like ECC over GF(2^n) with a polynomial base representation. This approach, combined with
the Schnorr digital signature algorithm (patent expired in 2008), is used in our library to generate small, secure product keys.
A license key management library
I will present a software development kit which can be used to both generate and validate product keys of various sizes and strengths. The SDK is written
in both C++ and C#, such that it is available in both native and managed form. It has interfaces in C++, C, and managed code.
The concept of "License Key Template"
A license key generally has the form XXXXX-XXXXX-XXXXX-XXXXX-XXXXX. It is comprised of a number of character groups, each group having a certain number of characters.
Internally, the license key contains binary data and a digital signature of that data. A license key template is a collection of parameters specifying how the license
key is encoded, how many groups of characters it has, how many characters per group, the size of the data we want to embed, and the size of the signature.
The sample code below uses the C++ API of the library.
LicenseKeyTemplate tmpl;
tmpl.SetNumberOfGroups(5);
tmpl.SetCharactersPerGroup(5); tmpl.SetGroupSeparator("-"); tmpl.SetSignatureSize(109); tmpl.SetDataSize(16); tmpl.AddDataField("ProductId", FIELD_TYPE_INTEGER, 16, 0);
The template parameters can also be exported to XML and imported from XML.
tmpl.LoadXml(strXml); strXml = tmpl.SaveXml(bSavePrivateKey);
Recommended signature sizes
When you set a certain product key signature size via tmpl.SetSignatureSize()
, the library automatically chooses a set of encryption parameters such that
the resulting signature is of the specified size. The following is a list of recommended signature sizes for use in generating license keys:
- 86 bits - This signature size uses a 54-bit ECC key and a 32-bit hash function. It allows for very small license keys (5 groups of 4 characters, or 4 groups of 5 characters,
or a single group of 20 characters, etc.). It can be secure enough for small projects, and it's certainly not so vulnerable to reverse engineering like a hidden symmetrical key.
If you choose your keys to have 5 groups of 4 characters (100 bits in total), you can use the remaining 14 bits to store various data.
- 109 bits - This size uses a 73-bit ECC key and a 36-bit hash function. It has similar strength to the Microsoft Windows keys. If you choose your
keys to have 5 groups of 5 characters (125 bits), you will also have 16 bits for various data you may want to embed in the keys (like a product ID,
bits for various features, etc.). Of course, you can choose your license keys to be larger (like 5 groups of 6 characters or whatever) if you need to store more data in the generated keys.
- 161 bits - This size uses a 97-bit ECC key and a 64-bit hash function. If you choose your keys to have 6 groups of 6 characters (180 bits in total),
you also have 19 bits remaining to store various data.
- 322 bits - This is the maximum possible size. It uses an 161-bit ECC key and the resulting keys are very secure but also a bit large.
Generating license keys
Once we have set up a template for the license keys we want to generate, we are ready to generate keys. But first, we must set the private key in the license key template:
tmpl.SetPrivateKey(strBase64Key);
If we are generating keys for the first time, we can generate a private/public key pair and store them securely for future use:
tmpl.GenerateSigningKeyPair();
tmpl.GetPrivateKey(privateKeyBuf, &privateBufLen);
tmpl.GetPublicKey(publicKeyBuf, &publicBufLen);
Now we are ready to generate the license keys:
LicenseKeyGenerator keyGen; keyGen.SetLicenseKeyTemplate(tmpl);
const char * key = keyGen.GenerateLicenseKey(); printf("%s", key);
Generating license keys locked to a specific customer name or computer
Sometimes we need to generate license keys locked to a specific customer name. For example, we want the customer to enter both his name and his license key
in the registration dialog, and we want the license key to be valid only for that customer name. In this way, we can prevent sharing a product key among many users,
because a customer may not be willing to publicly share his full name along with the product key.
Introducing the concept of validation data fields: this data does is not embedded in the license key like the key data, but it must be supplied at validation time.
At license key generation time, the license key signature is computed from both the key data and validation data, concatenated together.
At validation time, the validation data used at generation time must be supplied in order to successfully match the license key signature.
Programmatically, we just need to add a validation field in the license key template:
tmpl.SetValidationDataSize(1024);
tmpl.AddValidationField("CustomerName", FIELD_TYPE_STRING, 1024, 0);
When generating keys, the validation field must be set for each generated key. For example:
for (int i = 0; i < numberOfKeysToGenerate; i++)
{
keyGen.SetValidationData("CustomerName", customerNamesStringArray[i]);
keyGen.GenerateLicenseKey();
}
Another scenario is when we want a license key to be locked to a specific computer. If we have the means to obtain a computer hardware ID from our software (for example, the first
hard drive's serial number), we can use this as a validation field. Of course, the hardware ID must be supplied both at generation time and at validation time.
tmpl.AddValidationField("HardwareId", FIELD_TYPE_STRING, 1024, 0);
keyGen.SetValidationData("HardwareId", strHardwareId);
keyGen.GenerateLicenseKey();
keyValidator.SetValidationData("HardwareId", GetComputerHardwareId());
keyValidator.SetLicenseKey(strLicenseKey);
if (keyValidator.IsLicenseKeyValid())
{
} else
{
}
Validating license keys
For license key validation, we must first set the public key in the key template:
tmpl.SetPublicKey(strBase64PublicKey);
Now we are ready to validate keys:
LicenseKeyValidator keyVal;
keyVal.SetLicenseKeyTemplate(tmpl);
keyVal.SetLicenseKey(strLicenseKey);
if (keyVal.IsLicenseKeyValid())
{
keyVal.QueryLicenseKeyData("ProductId", dataBuf, &dataBufLen);
} else
{
}
Note: Although this it is beyond the scope of this article, the license key validation must, at a minimum, be done in multiple places within the source code
and at multiple times during the program execution in order to harden the license enforcement process.
Using the code
The provided library (library source code, sample source code, and binaries) may be freely used, but please note that some key source code files containing
the Elliptic Curve Cryptography algorithms are not included here. If you need full control over your licensing (a good idea), you can obtain the complete SDK
with full source code from http://www.softactivate.com. Most of the time it's not a good idea to use a DLL containing
your license key validation code because it can easily be hacked/replaced with a bogus DLL, it's better to embed the library code into your product directly.
History