Looking for a way to limit software use to weeks then allow client to reactivate with new serial?

We have currently had a Unity program made for PC and Mac to sell to clients. The software requires a serial code to activate but then runs permanently. I was wondering is there a way to give a client a serial number that would make the software work for x amount of time then stop working, however with a new code (different) get it working again for x amount of time, and we decide the x amount of time for its use?

Looking for a solution…

Well you just have to encode the desired timeout time in the serialkey. The problem with such a system is that your clients can quite easily (if they are a little bit familiar with .NET) decompile your code and either figure out how your serial check works, so the can produce valid keys on their own, or they can even change your original code and completely remove the serial check.

That’s why almost nobody uses those old “simple serialkeys” anymore. Most use an authenication that is account and server bound.

However if you want to implement such a simple serial key, just do something like this:

using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;

public static class SerialGenerator
{
    static RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
    static MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

    public enum SerialResult
    {
        Valid,
        Invalid,
        Expired
    }

    public static byte[] GenerateSerial(System.DateTime aExpires)
    {
        long date = aExpires.Ticks;
        byte[] bytes = System.BitConverter.GetBytes(date);
        if (System.BitConverter.IsLittleEndian)
            System.Array.Reverse(bytes); 
        byte[] randomData = new byte[8];
        rnd.GetBytes(randomData);
        bytes = bytes.Concat(randomData).ToArray();
        byte[] hash1 = md5.ComputeHash(bytes);
        byte[] hash2 = md5.ComputeHash(hash1);
        byte[] result = new byte[32];
        for (int i = 0; i < 16; i++)
            result[i] = hash1[i];
        for (int i = 0; i < 16; i++)
            result[i + 16] = (byte)(bytes[i] ^ hash2[i]);
        return result;
    }
    public static System.TimeSpan T;
    public static SerialResult ValidateSerial(byte[] aSerial)
    {
        long t = long.MinValue;
        
        if (aSerial.Length != 32)
            return SerialResult.Invalid;
        byte[] hash2 = md5.ComputeHash(aSerial,0,16);
        byte[] data = new byte[16];
        for(int i = 0; i < 16; i++)
            data[i] = (byte)(aSerial[16+i] ^ hash2[i]);
        byte[] bytes = new byte[8];
        System.Array.Copy(data, bytes, 8);
        if (System.BitConverter.IsLittleEndian)
            System.Array.Reverse(bytes);
        long date = System.BitConverter.ToInt64(bytes,0);
        byte[] hash1 = md5.ComputeHash(data);
        for(int i = 0; i < 16; i++)
            if (aSerial[i] != hash1[i])
                return SerialResult.Invalid;
        if (System.DateTime.UtcNow.Ticks > date)
            return SerialResult.Expired;
        return SerialResult.Valid;
    }
    public static string GenerateStringSerial(System.DateTime aExpires)
    {
        var serial = GenerateSerial(aExpires);
        return System.Convert.ToBase64String(serial);
    }
    public static SerialResult ValidateStringSerial(string aSerial)
    {
        try
        {
            var data = System.Convert.FromBase64String(aSerial);
            return ValidateSerial(data);
        }
        catch
        {
            return SerialResult.Invalid;
        }
    }
}

With that little class you can create serials which are only valid until the specified expiration time. To make this whole thing international i used the UTC time, so when creating serials keep that in mind.

Create a serial in base64 format which expires in 2 hours and 30 seconds from the moment you create the serial:

string serial = SerialGenerator.GenerateStringSerial(System.DateTime.UtcNow.AddHours(2).AddSeconds(30));

To validate a serial you would simply use:

var state = SerialGenerator.ValidateStringSerial(serial);
if (state == SerialGenerator.SerialResult.Expired)
{
    Debug.Log("the serial has expired");
}
else if (state == SerialGenerator.SerialResult.Valid)
{
    Debug.Log("the serial is valid");
}
else if (state == SerialGenerator.SerialResult.Invalid)
{
    Debug.Log("invalid serial");
}
else
{
    Debug.Log("strange internal error, should never happen :D");
}

Keep in mind that self-validating serials like this can be cracked once you know how it’s calculated. Since .NET can be easily decompiled that’s actually quite easy. It prevents the common user from using the software, but it’s not very safe.

edit
Just changed the class slightly to take the local endianness into account, otherwise a serial generaten on PC might not work on Mac.

Hello Bunny83,
many thx for the script but it doesn’t work on Windows Phone 8.1.
I get errors when i want to build in Unity.
Like The Typ- andNamespacename ‘RNGCryptoServiceProvider’ could not be found.
How can i fix the problem?
many thx
M.