97

How can I create a product key for my C# Application?

I need to create a product (or license) key that I update annually. Additionally I need to create one for trial versions.

Related:

2

15 Answers 15

86

You can do something like create a record which contains the data you want to authenticate to the application. This could include anything you want - e.g. program features to enable, expiry date, name of the user (if you want to bind it to a user). Then encrypt that using some crypto algorithm with a fixed key or hash it. Then you just verify it within your program. One way to distribute the license file (on windows) is to provide it as a file which updates the registry (saves the user having to type it).

Beware of false sense of security though - sooner or later someone will simply patch your program to skip that check, and distribute the patched version. Or, they will work out a key that passes all checks and distribute that, or backdate the clock, etc. It doesn't matter how convoluted you make your scheme, anything you do for this will ultimately be security through obscurity and they will always be able to this. Even if they can't someone will, and will distribute the hacked version. Same applies even if you supply a dongle - if someone wants to, they can patch out the check for that too. Digitally signing your code won't help, they can remove that signature, or resign it.

You can complicate matters a bit by using techniques to prevent the program running in a debugger etc, but even this is not bullet proof. So you should just make it difficult enough that an honest user will not forget to pay. Also be very careful that your scheme does not become obtrusive to paying users - it's better to have some ripped off copies than for your paying customers not to be able to use what they have paid for.

Another option is to have an online check - just provide the user with a unique ID, and check online as to what capabilities that ID should have, and cache it for some period. All the same caveats apply though - people can get round anything like this.

Consider also the support costs of having to deal with users who have forgotten their key, etc.

edit: I just want to add, don't invest too much time in this or think that somehow your convoluted scheme will be different and uncrackable. It won't, and cannot be as long as people control the hardware and OS your program runs on. Developers have been trying to come up with ever more complex schemes for this, thinking that if they develop their own system for it then it will be known only to them and therefore 'more secure'. But it really is the programming equivalent of trying to build a perpetual motion machine. :-)

2
  • 1
    Good summary. If anyone doesn't believe this is simple to bypass look up CheatEngine, it makes is so easy non-programmers can do it. Best to make this layer simple.
    – Kelly
    Commented Nov 27, 2012 at 0:03
  • I have the same problem, I made a license key for my app with the expiry date and the last logged date for verification, but the issue is that I have to add the private key to edit the file to update the last logged date which is not smart way to put the key in the code. any advice ?
    – Doicare
    Commented Jan 8, 2018 at 21:01
16

Who do you trust?

I've always considered this area too critical to trust a third party to manage the runtime security of your application. Once that component is cracked for one application, it's cracked for all applications. It happened to Discreet in five minutes once they went with a third-party license solution for 3ds Max years ago... Good times!

Seriously, consider rolling your own for having complete control over your algorithm. If you do, consider using components in your key along the lines of:

  • License Name - the name of client (if any) you're licensing. Useful for managing company deployments - make them feel special to have a "personalised" name in the license information you supply them.
  • Date of license expiry
  • Number of users to run under the same license. This assumes you have a way of tracking running instances across a site, in a server-ish way
  • Feature codes - to let you use the same licensing system across multiple features, and across multiple products. Of course if it's cracked for one product it's cracked for all.

Then checksum the hell out of them and add whatever (reversable) encryption you want to it to make it harder to crack.

To make a trial license key, simply have set values for the above values that translate as "trial mode".

And since this is now probably the most important code in your application/company, on top of/instead of obfuscation consider putting the decrypt routines in a native DLL file and simply P/Invoke to it.

Several companies I've worked for have adopted generalised approaches for this with great success. Or maybe the products weren't worth cracking ;)

3
  • 3
    FYI encryption is always reversible, it would be useless to not able to read what has been encrypted. Hashing is the one way 'encryption' you might be thinking of.
    – Samuel
    Commented Jan 18, 2009 at 6:39
  • "Don't roll your own crypto scheme", which I think is from Bruce Scheier (not sure), is the way to go. You may want to have a look at this answer: security.stackexchange.com/questions/2202/…
    – Shadok
    Commented Oct 3, 2011 at 15:11
  • Can you elaborate on "..P/Invoke to it". I looked at the linked page but it didn't made my any wiser :-/
    – MrCalvin
    Commented Aug 1, 2019 at 12:15
11

If you are asking about the keys that you can type in, like Windows product keys, then they are based on some checks. If you are talking about the keys that you have to copy paste, then they are based on a digitial signature (private key encryption).

A simple product key logic could be to start with saying that the product key consists of four 5-digit groups, like abcde-fghij-kljmo-pqrst, and then go on to specify internal relationships like f+k+p should equal a, meaning the first digits of the 2, 3 and 4 group should total to a. This means that 8xxxx-2xxxx-4xxxx-2xxxx is valid, so is 8xxxx-1xxxx-0xxxx-7xxxx. Of course, there would be other relationships as well, including complex relations like, if the second digit of the first group is odd, then the last digit of the last group should be odd too. This way there would be generators for product keys and verification of product keys would simply check if it matches all the rules.

Encryption are normally the string of information about the license encrypted using a private key (== digitally signed) and converted to Base64. The public key is distributed with the application. When the Base64 string arrives, it is verified (==decrypted) by the public key and if found valid, the product is activated.

10

Whether it's trivial or hard to crack, I'm not sure that it really makes much of a difference.

The likelihood of your app being cracked is far more proportional to its usefulness rather than the strength of the product key handling.

Personally, I think there are two classes of user. Those who pay. Those who don't. The ones that do will likely do so with even the most trivial protection. Those who don't will wait for a crack or look elsewhere. Either way, it won't get you any more money.

6

I have to admit I'd do something rather insane.

  1. Find a CPU bottleneck and extract it to a P/Invokeable DLL file.
  2. As a post build action, encrypt part of the DLL file with an XOR encryption key.
  3. Select a public/private key scheme, include public key in the DLL file
  4. Arrange so that decrypting the product key and XORing the two halves together results in the encryption key for the DLL.
  5. In the DLL's DllMain code, disable protection (PAGE_EXECUTE_READWRITE) and decrypt it with the key.
  6. Make a LicenseCheck() method that makes a sanity check of the license key and parameters, then checksums entire DLL file, throwing license violation on either. Oh, and do some other initialization here.

When they find and remove the LicenseCheck, what fun will follow when the DLL starts segmentation faulting.

3
  • Would that not then need DEP to be disabled? Commented Jan 22, 2009 at 11:04
  • No. Setting PAGE_EXECUTE_READWRITE is the documented correct way to write self-modifying code and clears NX bit on that page only.
    – Joshua
    Commented Jan 25, 2009 at 20:25
  • 9
    This general technique was very popular in the late 80s. Its weakness was that "secret" code is decrypted into RAM, making it easy to steal from any running copy of the software.
    – Ray Burns
    Commented Jun 12, 2010 at 0:09
5

There is the option Microsoft Software Licensing and Protection (SLP) Services as well. After reading about it I really wish I could use it.

I really like the idea of blocking parts of code based on the license. Hot stuff, and the most secure for .NET. Interesting read even if you don't use it!

Microsoft® Software Licensing and Protection (SLP) Services is a software activation service that enables independent software vendors (ISVs) to adopt flexible licensing terms for their customers. Microsoft SLP Services employs a unique protection method that helps safeguard your application and licensing information allowing you to get to market faster while increasing customer compliance.

Note: This is the only way I would release a product with sensitive code (such as a valuable algorithm).

1
  • For those remembering it as cancelled: SLP is relaunched again Commented Oct 14, 2011 at 16:27
5

If you want a simple solution just to create and verify serial numbers, try Ellipter. It uses elliptic curves cryptography and has an "Expiration Date" feature so you can create trial verisons or time-limited registration keys.

2

Another good inexpensive tool for product keys and activations is a product called InstallKey. Take a look at www.lomacons.com

2

One simple method is using a Globally Unique Identifier (GUID). GUIDs are usually stored as 128-bit values and are commonly displayed as 32 hexadecimal digits with groups separated by hyphens, such as {21EC2020-3AEA-4069-A2DD-08002B30309D}.

Use the following code in C# by System.Guid.NewGuid().

getKey = System.Guid.NewGuid().ToString().Substring(0, 8).ToUpper(); //Will generate a random 8 digit hexadecimal string.

_key = Convert.ToString(Regex.Replace(getKey, ".{4}", "$0/")); // And use this to separate every four digits with a "/".

I hope it helps.

1

The trick is to have an algorithm that only you know (such that it could be decoded at the other end).

There are simple things like, "Pick a prime number and add a magic number to it"

More convoluted options such as using asymmetric encryption of a set of binary data (that could include a unique identifier, version numbers, etc) and distribute the encrypted data as the key.

Might also be worth reading the responses to this question as well

7
  • 7
    "The trick is to have an algorithm that only you know" - this is pretty much the definition of security by obscurity, and a really bad idea. Commented Jan 18, 2009 at 12:26
  • 4
    All licensing is by an algorithm involving secrets though. Licensing is often best approached by investing in lawyers, rather than the arms race of coming up with "unbreakable" keys Commented Jan 18, 2009 at 16:36
  • +1 for the comment regarding licence enforcement via legal means
    – Rob
    Commented Jan 19, 2009 at 7:45
  • Yes, all licensing is weak, just like DRM. Relying on a secret algorithm is demonstrably weaker, though. Commented Jan 19, 2009 at 10:17
  • 1
    I gave you +1 for a good answer, and wish I give you another too counter the down vote. Sadly there are some very immature little babies in the world.
    – ProfK
    Commented Dec 29, 2011 at 6:14
1

There are some tools and API's available for it. However, I do not think you'll find one for free ;)

There is for instance the OLicense suite: http://www.olicense.de/index.php?lang=en

1

Please check this answer: https://stackoverflow.com/a/38598174/1275924

The idea is to use Cryptolens as the license server. Here's a step-by-step example (in C# and VB.NET). I've also attached a code snippet for key verification below (in C#):

var licenseKey = "GEBNC-WZZJD-VJIHG-GCMVD";
var RSAPubKey = "{enter the RSA Public key here}";

var auth = "{access token with permission to access the activate method}";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
    Key = licenseKey,
    ProductId = 3349,
    Sign = true,
    MachineCode = Helpers.GetMachineCode()
});

if (result == null || result.Result == ResultType.Error ||
    !result.LicenseKey.HasValidSignature(RSAPubKey).IsValid())
{
    // an error occurred or the key is invalid or it cannot be activated
    // (eg. the limit of activated devices was achieved)
    Console.WriteLine("The license does not work.");
}
else
{
    // everything went fine if we are here!
    Console.WriteLine("The license is valid!");
}

Console.ReadLine();
0

You can check LicenseSpot. It provides:

  • Free Licensing Component
  • Online Activation
  • API to integrate your app and online store
  • Serial number generation
  • Revoke licenses
  • Subscription Management
1
  • 1
    "Free" is not really free. It's free to embed the licensing component into your app; it's not free for the app to actually use the licensing component. Beyond 10 activations, You need to pay a monthly fee. It is not a per-activation percentage. For low-volume low-cost .NET apps, this pricing model is going to be a disadvantage. It ain't like the Apple AppStore for .NET apps.
    – Cheeso
    Commented Aug 19, 2011 at 21:00
0

I'm going to piggyback a bit on @frankodwyer's great answer and dig a little deeper into online-based licensing. I'm the founder of Keygen, a licensing REST API built for developers.

Since you mentioned wanting 2 "types" of licenses for your application, i.e. a "full version" and a "trial version", we can simplify that and use a feature license model where you license specific features of your application (in this case, there's a "full" feature-set and a "trial" feature-set).

To start off, we could create 2 license types (called policies in Keygen) and whenever a user registers an account you can generate a "trial" license for them to start out (the "trial" license implements our "trial" feature policy), which you can use to do various checks within the app e.g. can user use Trial-Feature-A and Trial-Feature-B.

And building on that, whenever a user purchases your app (whether you're using PayPal, Stripe, etc.), you can generate a license implementing the "full" feature policy and associate it with the user's account. Now within your app you can check if the user has a "full" license that can do Pro-Feature-X and Pro-Feature-Y (by doing something like user.HasLicenseFor(FEATURE_POLICY_ID)).

I mentioned allowing your users to create user accounts—what do I mean by that? I've gone into this in detail in a couple other answers, but a quick rundown as to why I think this is a superior way to authenticate and identify your users:

  1. User accounts let you associate multiple licenses and multiple machines to a single user, giving you insight into your customer's behavior and to prompt them for "in-app purchases" i.e. purchasing your "full" version (kind of like mobile apps).
  2. We shouldn't require our customers to input long license keys, which are both tedious to input and hard to keep track of i.e. they get lost easily. (Try searching "lost license key" on Twitter!)
  3. Customers are accustomed to using an email/password; I think we should do what people are used to doing so that we can provide a good user experience (UX).

Of course, if you don't want to handle user accounts and you want your users to input license keys, that's completely fine (and Keygen supports doing that as well). I'm just offering another way to go about handling that aspect of licensing and hopefully provide a nice UX for your customers.

Finally since you also mentioned that you want to update these licenses annually, you can set a duration on your policies so that "full" licenses will expire after a year and "trial" licenses last say 2 weeks, requiring that your users purchase a new license after expiration.

I could dig in more, getting into associating machines with users and things like that, but I thought I'd try to keep this answer short and focus on simply licensing features to your users.

-1

You can use HMAC to accomplish this. With HMAC, when you hash something you also use a secret key and this helps you verify the integrity and authenticity of the message/serial key. It means you verify that a license key is authentic and one that you have generated with your secret string.

HMAC is cool because its super simple. Its not like public key crypto. Its symetric which means all you need to do is generate one secret string and that key can be used to both generate hashes and also validate them later on. This is exactly what you need when creating product keys.

To get started, first you need to generate a secret key:

string secretKey = "mySecr3tKey!"

Then you would generate a serial/product key. For the key, I am going to split it into groups of 5 characters. The serial key will be 15 characters in length and the HMAC will also be 15 characters in length. We will combine them together.

To generate the serial key, I will use a GUID Like this:

var serialKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);
// Example Output = 9F1264CB63D54C9

Once you have the serial key, you would then generate an HMAC hash of the serial using the secret key that is known only to you. Like this:

var hmacToStore = CalculateHmac(serialKey, secretKey);
// Example Output = EE12A29DA5F2500

Now we have the serial/product key and the HMAC we would combine them to create the full serial. For example:

Generated Serial Key = 9F1264CB63D54C9
Generated HMAC       = EE12A29DA5F2500
Full Serial Key      = 9F126-4CB63-D54C9-EE12A-29DA5-F2500

Now, when the user enters the full serial key, to validate the key you can split the serial number from the HMAC. Then using our secret private key generate a new HMAC from the serial number. If the generated HMAC matches the HMAC from the license key, you know its valid.

var isValid = ValidateLicense(userLicense, secretKey);

Here's the full code listing that demonstrates the above:

using System;
using System.Security.Cryptography;
using System.Text;
                
public class Program
{
    public static void Main()
    {
        //var base64Secret = CreatePrivateKey();
        var base64Secret = "ADmDcqb8KQB/OE1/6VM+kJra6UE0VBcw3uRz4dN268Y7M/UsiSz0FuGSsfNkasmDQFAvJIUUEdB2EBzxtrd0tQ==";

        var licenseKey = CreateLicense(base64Secret);

        var isValid = ValidateLicense(licenseKey, base64Secret);
                    
        Console.WriteLine($"Is Licsense Valid = {isValid}");
    }
    
    static string CreatePrivateKey()
    {
        byte[] secretKey = GenerateRandomCryptographicBytes(64);

        // Convert key to base64 so can easily store it on the database.
        // This key should be kept private.
        var base64Secret = Convert.ToBase64String(secretKey );
        
        Console.WriteLine($"Private Key = {base64Secret}");

        return base64Secret;
    }
    
    static string CreateLicense(string secretKey)
    {
        var licenseKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);

        Console.WriteLine($"licenseKey = {licenseKey}");

        // Generate a Hmac license using secretkey
        var storedHmacOnDB = CalculateHmac(licenseKey, secretKey).ToUpper();
        var HMACTruncated = storedHmacOnDB.Substring(0, 15);

        Console.WriteLine($"HMAC = {HMACTruncated}");

        var licenseAndHMAC = InsertHyphen($"{licenseKey}{HMACTruncated}");

        Console.WriteLine($"Final User Licsense = {licenseAndHMAC}");

        return licenseAndHMAC;
    }
    
    static bool ValidateLicense(string licenseKey, string secretKey)
    {
        var tmp = licenseKey.Split('-');

        var license = $"{tmp[0]}{tmp[1]}{tmp[2]}";
        var licenseHmac = $"{tmp[3]}{tmp[4]}{tmp[5]}";

        string calculatedHmac = CalculateHmac(license, secretKey);
        var HMACTruncated = calculatedHmac.ToUpper().Substring(0, 15);

        bool isValid = licenseHmac.Equals(HMACTruncated, StringComparison.OrdinalIgnoreCase);

        return isValid;
    }
    
    static string InsertHyphen(string input, int everyNthChar = 5)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < input.Length; i++)
        {
            sb.Append(input[i]);
            if ((i + 1) % everyNthChar == 0 && i != input.Length - 1)
            {
                sb.Append("-");
            }
        }
        return sb.ToString();
    }
    
    static byte[] GenerateRandomCryptographicBytes(int keyLength)
    {
        byte[] key = new byte[64];
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(key);
        }
        return key;
    }
    
    static string CalculateHmac(string data, string hashKeyBase64)
    {
        var byteArray = Convert.FromBase64String(hashKeyBase64);
        return CalculateHmac(data, byteArray);
    }

    static string CalculateHmac(string data, byte[] hashKey)
    {
        var hmac = new HMACMD5(hashKey);
        byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
        }
    }
}

The above code will generate a 15 character serial, a 15 character HMAC and then combine them into a 30 character final serial key, like this:

Serial = A273C22C9FD84DF
HMAC = 301E028243A25B2
Final Serial = A273C-22C9F-D84DF-301E0-28243-A25B2

If you pass the serial through to the ValidateLicense function it will tell you if its valid or not based on your secretKey

var isValid = ValidateLicense("19460-63CC4-BB45F-6F6E6-4A983-DC0B1", base64Secret);

Ideally, the validate function and your secret key should be server side. The client app should call an API and pass the product key to the server to validate. If your stored the secret in your app, a nefarious user could attempt to disassemble the code to try to steal your private key.

1
  • The problem with HMAC is that the secret key is in the code, so it's easily discovered.
    – skst
    Commented Mar 19 at 4:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.