none
How to generate and validate a software license key using C#?

    Question

  • Could you please explain or provide any sample,  how to generate and validate a software license key using C#?
    Wednesday, June 24, 2009 1:00 PM

Answers

  • Could you please explain or provide any sample,  how to generate and validate a software license key using C#?

    Hi – I think my information can help you

    Think of “algorithms” in different forms. Hash algorithms (e.g. MD5 and other) can be used. In order, to prevent crack and hacking, we should store the algorithm has inside Windows® registry (Microsoft® Corporation stores their Windows® Product Serial Key inside Windows® registry. However, they are using a lot more advanced algorithm).


    The key should be stored in this form:

    User->Enters Product Key->The “Key” entered gets computed into HASH string data. Then the hash gets verified with another algorithm has inside Windows® registry. If the hash is “correct” then it will re-update the other hash (the stored one), with the new one.

    ALSO: MAKE SURE TO UPDATE AND ENCRYPT YOUR APPLICATION SOURCE CODE, SINCE NOT MANY WINDOWSAPPLICATIONS (*.EXES) ARE ENCRYPTED LIKE NORMAL WINDOWS BASED C/C++ APPS.


    I hope the above information was helpful…

    Have a nice day…

    Best regards,
    Fisnik    


    Coder24.com
    • Edited by Fisnik Hasani Thursday, June 25, 2009 8:55 AM Text Quality
    • Marked as answer by Harry Zhu Tuesday, June 30, 2009 9:10 AM
    Thursday, June 25, 2009 8:54 AM
  • It could be anything really, from just adding all the values in the registration name to some complicated encryption algorithms. Writing a license system isn't any different from writing any other code, you research what you want and solutions on the market already, you design a system or decide to buy a commercial solution and you implement it. If you want to write it your self there's tons of examples on the internet already google is your friend here. However do realize not matter how well you protect your application someone with enough determination and time on his hands will always crack it
    • Marked as answer by Harry Zhu Tuesday, June 30, 2009 9:10 AM
    Wednesday, June 24, 2009 2:37 PM

All replies

  • It could be anything really, from just adding all the values in the registration name to some complicated encryption algorithms. Writing a license system isn't any different from writing any other code, you research what you want and solutions on the market already, you design a system or decide to buy a commercial solution and you implement it. If you want to write it your self there's tons of examples on the internet already google is your friend here. However do realize not matter how well you protect your application someone with enough determination and time on his hands will always crack it
    • Marked as answer by Harry Zhu Tuesday, June 30, 2009 9:10 AM
    Wednesday, June 24, 2009 2:37 PM
  • Could you please explain or provide any sample,  how to generate and validate a software license key using C#?

    Hi – I think my information can help you

    Think of “algorithms” in different forms. Hash algorithms (e.g. MD5 and other) can be used. In order, to prevent crack and hacking, we should store the algorithm has inside Windows® registry (Microsoft® Corporation stores their Windows® Product Serial Key inside Windows® registry. However, they are using a lot more advanced algorithm).


    The key should be stored in this form:

    User->Enters Product Key->The “Key” entered gets computed into HASH string data. Then the hash gets verified with another algorithm has inside Windows® registry. If the hash is “correct” then it will re-update the other hash (the stored one), with the new one.

    ALSO: MAKE SURE TO UPDATE AND ENCRYPT YOUR APPLICATION SOURCE CODE, SINCE NOT MANY WINDOWSAPPLICATIONS (*.EXES) ARE ENCRYPTED LIKE NORMAL WINDOWS BASED C/C++ APPS.


    I hope the above information was helpful…

    Have a nice day…

    Best regards,
    Fisnik    


    Coder24.com
    • Edited by Fisnik Hasani Thursday, June 25, 2009 8:55 AM Text Quality
    • Marked as answer by Harry Zhu Tuesday, June 30, 2009 9:10 AM
    Thursday, June 25, 2009 8:54 AM
  • If you are willing to try a commercial licensing solution then check Ellipter.
    It creates short product keys with embedded product info and/or expiration time handled automatically and is based on elliptic curves asymmetric cryptography.

    Saturday, December 12, 2009 11:58 AM
  • Nice tool. However, I prefer using my customized solutions.
    Coder24.com
    Saturday, December 12, 2009 12:02 PM
  • I tried the Ellipter software and found it wanting in many areas.  I've since switched to License Vault from spearmantech.  

    After going through all the trouble of creating a hashing algorithm how do you prevent your end-user some massively distributing that key so all his/her known associates won't use your application as well.  

    In the end you'll have to switch to a commercial solution and it's better to do that at first than later on when you've already distributed previous versions of your application with that security flaw.  
    Wednesday, January 06, 2010 6:26 AM
  • Catalin,

    You should be upfront and disclose that you are the owner of SoftActivate.

    Thursday, December 29, 2011 2:14 PM
  • I am late to this post, but I would like to introduct License IT by www.datajuggler.com (my company).

    License IT isn't free, but it is cheaper than all of the license systems I have found.

    You can download a trial from www.datajuggler.com

    or watch a movie on YouTube:

    https://www.youtube.com/watch?v=BycOw5KPCr4

    License IT comes with a Windows and WPF sample.

    License IT is affordably priced and easy to setup:

    Standard:     $50
    Professional: $100
    Enterprise:   $200


    Corby

    Friday, April 26, 2013 2:42 AM
  • There are a few good ways of doing this. 

    1) If your like me, you want the key you give your customers to be verified before installation.  So your code should take their input (do a simple validation or two), and send it back to a SOAP or WCF endpoint to confirm that the key is good and still valid (just in case you decided to deny the key for some reason like finding it on the internet).  The best way to do this is to take a Guid and Base36 encode it so the 32 character Guid becomes 25 characters.  This way the customer won't have a cow saying "Its just to long".

    2) If you want you code to validate it offline (meaning the installer does not contact the Mother Ship to verify), you can take your same Guid, Hash and sign it with a RSA key from "The Mother Ship".  Then embed the public key within your installer to verify.  The customer would be given a much larger key that typically would be 256 bytes long (considering you use a RSA 2028 key vs 1024).  This method has some draw backs:

       a) The key you give the customer will be rather larger
       b) You can hash the key like some folks in this thread suggested, but any hash other than MD5 would again be larger then the typical 25 character product keys you see with Microsoft.

    Again, this is not an exhaustive listing of all the various ways of implementing product keys, but hopefully these trivial methods help raise everyone's understanding of how the big companies do it.  Its not rocket science for sure.

    - Rashad Rivera

    REF:

    [I] A Guid is a 16 byte value and running it through a good Base36 encoder which using the following key will give you the same Microsoft style product keys:
    Base36 Encoding Digits: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ (36 characters in all)

    [II] Some examples form my product key generator:
    bcc5a235-391b-484f-b81e-941b603b9e55 => 5GFGS-X7VU3-DO5CC-5Z45T-U5H25
    f595915f-1934-4cbe-a283-a86dc5ee7f63 => R5DRQ-ZDU0X-6G0FO-VN51F-092W5
    NOTE: The product keys is a direct (one for one) representation of the Guid it was generated from.  So that means you can't change one bit of the key and get the same Guid (or by random chance another valid Guid)

    BELOW IS THE CODE FOR MY PRODUCT KEY GENERATOR

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Numerics;
    
    namespace ConsoleApplication1 {
    
    	class Program {
    
    		static void Main(string[] args) {
    
    			var myBase36Digits = Base36Extensions.StandardBase36Digits; // using standard digits
    			//var myBase36Digits = "XACDFLE5IGU21RVP7Y0KJ9NO8WQHTZ63BMS4";
    			//var myBase36Digits = _GenerateCustomeBase36DigitKey();
    
    			var g = Guid.Parse("B25E72D2-C309-4D9E-83E8-66C1C4EA30B1");//Guid.NewGuid();
    			//var bytes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF };
    			//var bytes = new byte[] { 0x00, 0xFF };
    			var bytes = g.ToByteArray(); Console.WriteLine("Guid: {0}", g);
    			//var bytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
    			//var bytes = BitConverter.GetBytes((long)-3);
    
    			var base36 = bytes.ToBase36String(base36DigitKey: myBase36Digits);// bigEndian: bigEndian);
    			var reconstitutedBytes = base36.FromBase36String(base36DigitKey: myBase36Digits);// bigEndian: bigEndian);
    			Console.WriteLine("Base36: '{0}'", base36);
    			Console.WriteLine("Orig: {0}\r\nRecn: {1}", BitConverter.ToString(bytes), BitConverter.ToString(reconstitutedBytes));
    			Console.WriteLine();
    			Console.WriteLine();
    
    			bytes = bytes.Reverse().ToArray();
    			base36 = bytes.ToBase36String(base36DigitKey: myBase36Digits);// bigEndian: !bigEndian);
    			reconstitutedBytes = base36.FromBase36String(base36DigitKey: myBase36Digits);// bigEndian: !bigEndian);
    			Console.WriteLine("Orig: {0}\r\nRecn: {1}", BitConverter.ToString(bytes), BitConverter.ToString(reconstitutedBytes));
    			Console.WriteLine("Base36: '{0}'", base36);
    			Console.WriteLine();
    			Console.WriteLine();
    
    			g = Guid.Empty;
    
    			int i = 0;
    			while (true) {
    
                    bytes = g.ToByteArray();
    				base36 = bytes.ToBase36String(base36DigitKey: myBase36Digits);
    				reconstitutedBytes = base36.FromBase36String(base36DigitKey: myBase36Digits);
    				if (!bytes.SequenceEqual(reconstitutedBytes)) {
    					Console.WriteLine("Failed for Guid value: {0}", g);
    					break;
    				}
    
    				bytes = bytes.Reverse().ToArray();
    				var base36o = bytes.ToBase36String(base36DigitKey: myBase36Digits);
    				reconstitutedBytes = base36o.FromBase36String(base36DigitKey: myBase36Digits);
    				if (!bytes.SequenceEqual(reconstitutedBytes)) {
    					Console.WriteLine("Failed for *INVERTED* Guid value: {0}", g);
    					break;
    				}
    
    				Console.WriteLine("Test {0,10} PASSED      {1} => {2} ", i++, g, g.ToProductKey());
    
    				g = Guid.NewGuid();
    			};
    
    			Console.ReadKey(true);
    		}
    
    		static string _GenerateCustomeBase36DigitKey() {
    
    			var basicBase36Digits = Base36Extensions.StandardBase36Digits;
    			var rnd = new Random();
    			var list = new List<char>();
    			do {
    
    				var index = rnd.Next(36);
    				var c = basicBase36Digits[index];
    				if (!list.Contains(c))
    					list.Add(c);
    				if (list.Count() == 35) {
    					foreach (var c2 in basicBase36Digits)
    						if (!list.Contains(c2))
    							list.Add(c2);
    				}
    
    			} while (list.Count() < 36);
    
    			var returnValue = new string(list.ToArray());
    			return returnValue;
    		}
    	}
    
    	public static class ProductKeyExtensions {
    
    		public static string ToProductKey(this Guid target) {
    
    			string productKey;
    			var data = target.ToByteArray();
    			if (!_IsProductKeyCapable(data, productKey: out productKey))
    				return "n/a";
    
    			var returnValue = string.Format("{0}-{1}-{2}-{3}-{4}",
    				productKey.Substring(0, 5),
    				productKey.Substring(5, 5),
    				productKey.Substring(10, 5),
    				productKey.Substring(15, 5),
    				productKey.Substring(20, 5)
    			);
    
    			return returnValue;
    		}
    
    		public static bool IsProductKeyCapable(this Guid target) {
    
    			var data = target.ToByteArray();
    			string productKey;
                return _IsProductKeyCapable(data, productKey: out productKey);
    		}
    
    		static bool _IsProductKeyCapable(byte[] data, out string productKey) {
    
    			if (data == null)
    				throw new ArgumentNullException("data");
    			if (data.Length != 16)
    				throw new ArgumentException("Data byte array must be 16 character's long.", "data");
    
    			productKey = data.ToBase36String();
    			return productKey.Length == 25;
    		}
    	}
    
    	public static class Base36Extensions {
    
    		#region Constant(s)
    
    		const int ByteBitCount = 8; // number of bits in a byte
    		public const string StandardBase36Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    		static readonly double Base36CharsLengthDivisor = Math.Log(StandardBase36Digits.Length, 2);
    		static readonly BigInteger BigInt36 = new BigInteger(36);
    
    		#endregion
    
    		#region Method(s)
    
    		public static string ToBase36String(this byte[] bytes) {
    
    			if (bytes == null)
    				throw new NullReferenceException();
    
    			var returnValue = _ToBase36String(bytes, base36DigitKey: StandardBase36Digits);
    			return returnValue;
    		}
    
    		public static string ToBase36String(this byte[] bytes, string base36DigitKey) {
    
    			if (bytes == null)
    				throw new NullReferenceException();
    			if (base36DigitKey.Length != 36)
    				throw new ArgumentNullException("base36Digits", "Base 36 digit key must be exactly 36 characters long");
    			if (base36DigitKey.Contains("+"))
    				throw new ArgumentException("Base-36 digit key cannot contain the '+' (plus) symbol.", "base36Digits");
    
    			var duplicates = base36DigitKey
    				.GroupBy(i => i)
    				.Where(g => g.Count() > 1)
    				.Select(g => g.Key)
    				.ToArray();
    
    			if (duplicates.Count() > 0)
    				throw new ArgumentException(string.Format("Character '{0}' cannot exist more than once.", duplicates[0]), "base36DigitKey");
    
    			var returnValue = _ToBase36String(bytes, base36DigitKey: base36DigitKey);
    			return returnValue;
    		}
    
    		public static byte[] FromBase36String(this string base36String) {
    
    			if (base36String == null)
    				throw new NullReferenceException();
    
    			var returnValue = _FromBase36String(base36String, base36DigitKey: StandardBase36Digits);
    			return returnValue.ToArray();
    		}
    
    		public static byte[] FromBase36String(this string base36String, string base36DigitKey) {
    
    			if (base36String == null)
    				throw new NullReferenceException();
    
    			if (base36DigitKey.Length != 36)
    				throw new ArgumentNullException("base36DigitKey", "Base 36 digit key must be exactly 36 characters long");
    			if (base36DigitKey.Contains("+"))
    				throw new ArgumentException("Base-36 digit key cannot contain the '+' (plus) symbol.", "base36DigitKey");
    
    			var duplicates = base36DigitKey
    				.GroupBy(i => i)
    				.Where(g => g.Count() > 1)
    				.Select(g => g.Key)
    				.ToArray();
    
    			if (duplicates.Count() > 0)
    				throw new ArgumentException(string.Format("Character '{0}' cannot exist more than once.", duplicates[0]), "base36DigitKey");
    
    			var returnValue = _FromBase36String(base36String, base36DigitKey: base36DigitKey);
    			return returnValue.ToArray();
    		}
    
    		#region Helper Method(s)
    
    		static byte[] _FromBase36String(string base36String, string base36DigitKey) {
    
    			if (string.IsNullOrEmpty(base36String))
    				return new byte[0];
    
    			var value = BigInteger.Zero;
    			var digitArray = base36DigitKey.ToArray();
    
    			var base36Array = base36String.ToList();
    
    			base36Array.Reverse();
    			var hasInsignificateByte = false;
    
    			foreach (var c in base36Array) {
    
    				if (!value.IsZero)
    					value *= BigInt36;
    
    				var index = digitArray.IndexOf(c);
    				if (index == -1) {
    					hasInsignificateByte = true;
    					continue;
    				}
    				value += new BigInteger(index);
    			}
    
    			IEnumerable<byte> returnValue = null;
    			returnValue = value.ToByteArray();
    
    			if (hasInsignificateByte)
    				returnValue = returnValue.Take(returnValue.Count() - 1);
    
    			return returnValue.ToArray();
    		}
    
    		static string _ToBase36String(byte[] bytes, string base36DigitKey) {
    
    			if (bytes == null)
    				throw new ArgumentNullException("bytes");
    			if (bytes.Length == 0)
    				return string.Empty;
    
    			int result_length = (int)Math.Ceiling(bytes.Length * ByteBitCount / Base36CharsLengthDivisor);
    			var result = new List<char>(result_length);
    
    			var insignificateByte = false;
    			var lastByte = bytes[bytes.Length - 1];
    			if (lastByte == 0 || (lastByte & 0x80) == 0x80) {
    				bytes = bytes.Concat(new byte[] { 0x01 }).ToArray();
    				insignificateByte = true;
    			}
    
    			var dividend = new BigInteger(bytes);
    			var copy = dividend.ToByteArray();
    			while (!dividend.IsZero) {
    				BigInteger remainder;
    				dividend = BigInteger.DivRem(dividend, BigInt36, out remainder);
    				int digit_index = Math.Abs((int)remainder);
    				result.Add(base36DigitKey[digit_index]);
    			}
    
    			if (insignificateByte)
    				result.Add('+');
    
    			var returnValue = new string(result.ToArray());
    			return returnValue;
    		}
    
    		#endregion
    
    		#endregion
    	}
    
    	public static class _IndexOfExtension {
    
    		public static int IndexOf<T>(this IEnumerable<T> target, T item) {
    
    			if (target == null)
    				throw new NullReferenceException();
    
    			var returnValue = 0;
    			foreach (var i in target) {
    				if (object.Equals(i, item))
    					return returnValue;
    				returnValue++;
    			}
    
    			return -1;
    		}
    	}
    }
    



    - Rashad Rivera www.omegusprime.com



    • Proposed as answer by Rashad Rivera Monday, November 23, 2015 2:49 AM
    • Edited by Rashad Rivera Monday, November 23, 2015 3:29 AM Fixed but and Added Randome Base36 Digit Key Capability (BAM!)
    Monday, November 23, 2015 2:48 AM