7

First and foremost, this is not a question about generating a serial number for other products. I am not looking to "hack" other products.

Here are my requirements:

  • I would like to generate a standard looking serial number: AILU7-ABCDE-54321-1234-AFCK-17UDF
  • I need some process that validates whether or not that serial number is "valid" - This portion does not have to be extremely complex. ie, if all the numbers sum up to a value greater than X - I'd be okay with that, but if there are solutions out there that handle this complexity for me, I'd be happy to use them.
  • The process that validates the serial number can not be server side. ie, I can not make a request out to an external web server to verify that the serial number is valid.
  • I need some way to pull out metadata from the serial number. ie, after I have validated that the serial number is correct, i need to be able to read out some values from it: "user limit", "expiration date", etc ...
  • The verification of the serial number will be done via a ASP.NET MVC 3 application. The generation of the serial number does not have to be done that way though.

I'm not looking for a silver bullet that will accomplish all these requirements, but more or less some links to documentation or existing libraries that will help me get started. The only library that I have seen is the XHEO DeployLX library; which is just entirely too much for my needs.

Could you please provide me with any information that could point me in the right direction?

Bryan Ray
  • 931
  • 10
  • 26
  • Have you seen that link here on SO: http://stackoverflow.com/questions/4583630/serial-numbers-generation-without-user-data – Simon Mourier Dec 12 '12 at 16:16
  • 4
    I'm not sure what you're trying to achieve is possible, embedding a company name in a short string for example is going to be really difficult if it's possible at all. If you need that extra metadata maybe you should consider a license file model instead or store a key->meta data mapping somewhere. – Ian Dec 12 '12 at 16:17
  • @Ian - I am completely okay with that as well. I just need information on how to get started with the model. Cryptography is foreign to me at the moment. – Bryan Ray Dec 12 '12 at 16:21
  • @Ian - Agreed. It's certainly possible to *verify* a company name or anything else from a short string, but *encoding* it is going to be far harder. It's a compression issue. – Bobson Dec 12 '12 at 16:24
  • @Ian Could you provide any links or information to the mentioned "License File Model" by any chance? My google-foo is not helping in this regards. – Bryan Ray Dec 12 '12 at 20:45
  • 1
    @BryanRay: I don't actually know of any examples (it's something I want to see if theres a common way of doing). The idea would be that you have a file with all your meta-information (company name, machine info etc...) and a generated ID. This would then be hashed using a private key by the provider. The application would verify using a public key that the file hasn't been modified by checking the hash, and can then read all the license information from it. – Ian Dec 13 '12 at 08:57

3 Answers3

5

Did you check out Brandon Staggs article on this very matter?

In Delphi, but theory holds whatever language.

Viktor Elofsson
  • 1,581
  • 2
  • 13
  • 20
4

Plenty of libraries out there, one would be http://skgl.codeplex.com/ & http://softwareprotector.codeplex.com/ and comes with a nuget package as well.

Ilya Kozhevnikov
  • 10,242
  • 4
  • 40
  • 70
0

With serial keys you need to consider a few things.

I have seen links during my hunts that point to using a simple Guid.NewGuid(); approach and then doing some transformations on the string to make a custom styled Serial key. This is easy to do, but puts the onus on you the product owner to keep track of serial keys in a database and at the end of the day there is potential for someone to randomly find serials that work via using Guid.NewGuid(); themselves. If everyone on the planet started generating Guids at the same time, collision chances become very likely.

There is a sort of solution that makes collision events less likely by using a more complex algorithm on top of Guid.NewGuid();

For this, I tend to use:

  1. Guid.NewGuid(); (First 16 characters only, minus the - (hyphen)
  2. An ever incrementing or changing value. (Nonce) (an int will work : i++ etc)
  3. A secret salt that you will keep private and secure in your network.
  4. A difficulty factor : borrowing this principle from bitcoin.

Ok, lets imagine I am taking the first 16 digits from a guid. I then combine that with the Nonce and the secret salt, then use SHA256 to derive a hash from the values. I can then use the Difficulty factor to determine if the hash begins with the amount of 0's or other character that I desire.

Eg: If the hash has six 0's prefixing it, then I save all of the data off, as I have just found a reasonably secure Serial key.

When I mean secure, I mean I have found a Serial, that when combined with a Product Key (the Nonce) and then used with the secret Salt, it results in a Hash that meets my production criteria.

Some example code is below - done very rough, because I was bored.

The idea would be that your application could send the product key and serial to an activation server. The server knows the secret salt. It then returns true or false to determine if the hash generated meets the security requirement. If it does not : the Serial is not valid, or not valid for the key provided. If it does have the required 0's : its a valid serial.

    Guid theGuid;
    string Hash = "";
    int iAccess = 0;
    string PrivateSalt = "Alpha";
    string SourceString = "";
    string guidString;
    while (true)
    {
        theGuid = Guid.NewGuid();
        guidString = theGuid.ToString().Replace("-", "").Substring(0,16);
        SourceString = guidString + "|" + iAccess.ToString() + "|" + PrivateSalt;
        byte[] data = Encoding.Default.GetBytes(SourceString);
        Hash = Crypto.GenerateSHA256(data);
        if (Hash.StartsWith(GetDiff()))
        {
            break;
        }

        iAccess++;
    }
    Console.WriteLine(SourceString+" Gives hash "+Hash);
    string s1, s2, s3, s4;
    s1 = guidString.Substring(0, 4);
    s2 = guidString.Substring(4, 4);
    s3 = guidString.Substring(8, 4);
    s4 = guidString.Substring(12, 4);
    string serial = s1 + "-" + s2 + "-" + s3 + "-" + s4;

    Console.WriteLine(serial + " :" + SourceString + " Gives hash " + Hash);

GetDiff() is basically just a string : "000000";

Example output from this method looks like this:

d9c9-f6f0-45be-427a :d9c9f6f045be427a|15135|Alpha Gives hash    000000f718f69c8389d496e01d1e992946fe1b8cf72bc4200a7a2b800b40aa0a
fe49-70b9-08d8-40df :fe4970b908d840df|9096414|Alpha Gives hash  000000e29cfccfb54d1e7edc816feb084f1a2cd11a20c3132a965f9048fc9bf4
7f58-0636-c853-4f0a :7f580636c8534f0a|12297217|Alpha Gives hash 0000007bb44f39a964bbe985885451c3dc0e037fcd12951261404e48819bf89b
6f65-82d3-d95b-4882 :6f6582d3d95b4882|15064854|Alpha Gives hash 000000f1a3bed79e441108cfd26d8733d3fc10f5cd66d234ed35fe2b769663a3
edee-b8b7-9f6f-40ab :edeeb8b79f6f40ab|17782415|Alpha Gives hash 000000b70b96e7b008a96a860efc572fe868154ae81e67b9397249a51f2db71c
0948-4bb3-7de4-4054 :09484bb37de44054|21105690|Alpha Gives hash 000000ec7317eccd5fd9bb701759a2b0e77d37099347d9d665f4b492a69ca3ec
bbf5-5119-bf4e-463c :bbf55119bf4e463c|21715642|Alpha Gives hash 000000a134c886d01606da83cd5e8f672fddb6aa061968e9f08202c781514b16
80f6-c9c5-0ddf-436d :80f6c9c50ddf436d|26450310|Alpha Gives hash 00000092305b2956381c23dacba5b8ff9a37ab994148b37677732dc2a0650386
0a4f-143b-b5f5-48ca :0a4f143bb5f548ca|33691865|Alpha Gives hash 00000054ecdae57c6ec686b6084faf68ae49a78f7c07bbe8e51357d76de63870

You can increase the difficulty by adding more 0's to the prefix. It means that finding serial combinations will take longer, but also makes it more secure.

Obviously you would store these data combinations away somewhere so during activation, you can compare the Serial Code, and the Product Key (Nonce).

In my example: I am using Serial Key (16 digits), Incrementing int, and the word Alpha for the secret salt.

This makes generating serial keys slow and cpu intensive, but makes validating them very fast.

IsSerialValid("edee-b8b7-9f6f-40ab", 17782415);

public bool IsSerialValid(string serialCode, int ProductCode)
        {
            string SourceString = serialCode.Replace("-", "") + "|" + ProductCode.ToString() + "|" + "Alpha";
            byte[] data = Encoding.Default.GetBytes(SourceString);
            string Hash = Crypto.GenerateSHA256(data);
            if (Hash.StartsWith(GetDiff()))
            {
                return true;
            }
            return false;
        }

The secret Salt could be a code phrase that maps to different products that you may be developing. This allows you to re-use product keys (Nonce) values across multiple product lines.

Baaleos
  • 1,703
  • 12
  • 22
  • the validation doesn't work always returns false, since the source string that used in the validation will be not same that used in generation the product code part will be diff. – Henka Programmer Jul 26 '19 at 18:17