Click here to Skip to main content
Email Password   helpLost your password?

Demo project screen-shot

Introduction and Background

I recently had to create an application with a registration system which would be used by many users at the same time, each of them purchasing their own license.

The idea was the following: the user purchases a license key, which he/she then enters into the application (which could be downloaded freely). The application would contact a central server, which would return an activation request serial number. This would then be sent to the license reseller, who would generate an activation key, which would in turn be entered into the application by the user.

I needed some way of uniquely identifying each computer with ease, to ensure that no two users could use the same license key, yet each user could establish several connections to the server.

In this article, I will show you how I accomplished this task.

The actual code

I solved the problem by making use of the computer name, username, and user domain, the number of processors, and the number of logical drives which are installed on the computer. This way, even a multi-user system would require each user to register their own copy of the application, but no user could use the same license on more than one computer.

First, create a new Windows application, and add a button plus textbox to your form.

In the button1_click routine, add the following code:

textBox1.Text =  // Set the contents of textBox1 to:

    Environment.ProcessorCount + "/" +       //

    // {number of processors}/{machine name}/

    Environment.MachineName + "/" + 
    // {user domain}\{username}/

    Environment.UserDomainName + "\\" +
    // {number of logical drives}
    Environment.UserName + "/" + 
    Environment.GetLogicalDrives().Length;   //

The code should be fairly easy to understand.

Points of Interest

The disadvantage to this method is that the same conditions can be simulated, but with some difficulties. This method should not be used where expensive software is licensed - I used this for cheap licensing of small software.

This method still needs some enhancements to be ready for big software.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAnother Simple and Reliable Alternative
Weifen Luo
15:26 1 Mar '10  
Take a look at my blog post:
http://www.devzest.com/blog/post/Local-Machine-Copy-Protected-Licensing.aspx[^]
GeneralUnique Machine ID
Anindya Chatterjee
5:09 3 Mar '09  
Hello guys,

I have created the following codeblock taking help from this article and elsewhere, may be it would be a good working version for getting a unique id for a machine ... check it and have some comments..


using System;
using System.Management;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.IO;
using System.Text;

namespace UniqueID
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine(GetUniqueID());

// TODO: Implement Functionality Here

Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}


public static string GetCPUId()
{
string cpuInfo = String.Empty;
string temp = String.Empty;
ManagementClass mc = new ManagementClass("Win32_Processor");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if (cpuInfo == String.Empty)
{// only return cpuInfo from first CPU
cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
}
}
return cpuInfo;
}

public static string GetMotherBoardID()
{
ManagementObjectCollection mbCol = new ManagementClass("Win32_BaseBoard").GetInstances();
//Enumerating the list
ManagementObjectCollection.ManagementObjectEnumerator mbEnum = mbCol.GetEnumerator();
//Move the cursor to the first element of the list (and most probably the only one)
mbEnum.MoveNext();
//Getting the serial number of that specific motherboard
return ((ManagementObject)(mbEnum.Current)).Properties["SerialNumber"].Value.ToString();
}

public static string GetMacAddress()
{
string macs = "";

// get network interfaces physical addresses
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in interfaces)
{
PhysicalAddress pa = ni.GetPhysicalAddress();
macs += pa.ToString();
}
return macs;
}

/// <summary> /// return Volume Serial Number from hard drive
/// </summary> /// <param name="strDriveLetter">[optional] Drive letter</param> /// <returns>[string] VolumeSerialNumber</returns> public static string GetVolumeSerial()
{
string strDriveLetter = "";

ManagementClass mc = new ManagementClass("Win32_PhysicalMedia");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
try {
if ((UInt16)mo["MediaType"] == 29)
{
String serial = mo["SerialNumber"].ToString().Trim();
if (!String.IsNullOrEmpty(serial))
{
Console.WriteLine("Harddrive serial: " + (string)mo["SerialNumber"]);
strDriveLetter = (string)mo["SerialNumber"];
return strDriveLetter;
}
}

}
catch{}

}
return strDriveLetter;
}


public static string GetUniqueID()
{
string ID = GetCPUId() + GetMotherBoardID() + GetMacAddress() + GetVolumeSerial();
// generate hash
HMACSHA1 hmac = new HMACSHA1();
hmac.Key = Encoding.ASCII.GetBytes(GetMotherBoardID());
hmac.ComputeHash(Encoding.ASCII.GetBytes(ID));

// convert hash to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hmac.Hash.Length; i++)
{
sb.Append(hmac.Hash[i].ToString("X2"));
}

return sb.ToString();

}

}
}



Anindya Chatterjee
--------------------------------------------------------
1. Don't Visit ..[^]

2. But Watch ..

AnswerMotherBoard Serial Number
Mad Philosopher
5:56 26 Jun '08  
Hi,

I'm newbie in programming, so, forgive any mistakes Shucks

I've been working on this too. I needed to get a unique ID per machine that can't be changed.
I tried the CPUID, But it's not Uniue. The MAC can be changed.
The CPU Serial was good, but as I have read on the web, it's not there anymore after P3.

The Motherboard Serial Number seems fine. Here is the way:

/// 
/// Return the serial number of the motherboard
///
/// [string] SN of motherboard public static string GetMBSN()
{
//Getting list of motherboards
ManagementObjectCollection mbCol = new ManagementClass("Win32_BaseBoard").GetInstances();
//Enumerating the list
ManagementObjectCollection.ManagementObjectEnumerator mbEnum = mbCol.GetEnumerator();
//Move the cursor to the first element of the list (and most probably the only one)
mbEnum.MoveNext();
//Getting the serial number of that specific motherboard
return ((ManagementObject)(mbEnum.Current)).Properties["SerialNumber"].Value.ToString();
}

The code above was inspired from:
- http://www.eggheadcafe.com/articles/20030511.asp
- http://bytes.com/forum/thread236091.html
GeneralRe: MotherBoard Serial Number
John Simmons / outlaw programmer
2:32 17 Jul '08  
Reliably identifying a given system is fraught with danger.

MAC addresses are unreliable for system ID. Even if the system contains one or more NICs, if ALL of the NICs are disabled, you won't be able to retrieve any MAC address. Besides that, they can be changed by the user.

The Win32_BaseBoard class contains the serial number of the board, but if the BIOS gets updated by the user, the chances are very good that the serial number property will be empty or null (the same goes for the Win32_BIOS class).

You also can't use Win32_ComputerSystemProduct class, because machines not built by a retail system builder (like Dell, Acer, Toshiba, HP, etc) will have a UUID of FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF.

I'm still looking for a reliable and repeatable way to identify a system.

"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001

GeneralRe: MotherBoard Serial Number
ChizI
13:31 4 Nov '09  
Just FYI, this is not reliable. Most network administrators build machines based on a ghost image and windows API gathers this information from registry. Registry is only updated one time, on install of Windows. Therefore you will get the same "Unique ID", on every machine that has been created from a ghost.

--
Chizl

GeneralNot really unique
Ray Cassick
17:24 11 Apr '06  
This seems to me to be something that could be easily worked around.



My Blog[^]
FFRF[^]


GeneralRe: Not really unique
Fabrice Vergnenegre
22:53 11 Apr '06  
Yes it's definitely not unique since all the parameters used can be changed easily to match a valid configuration...

Try to use some physical fixed strings, like the mac address of the network adapter, or the processor serial number, and some strings to identify the user. Tip: Generate a hash of a string composed of all the parameters used to identify the machine and the user. Using this technique, all your ids will have the same length and are not human readable, nor reversible (theoretically).

Ex (.NET 2.0) :
using System.Net.NetworkInformation;
using System.Text;
using System.Security.Cryptography;

...

private string GetUniqueId()
{
string macs = "";

// get network interfaces physical addresses
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in interfaces)
{
PhysicalAddress pa = ni.GetPhysicalAddress();
macs += pa.ToString();
}

// generate hash
HMACSHA1 hmac = new HMACSHA1();
hmac.Key = Encoding.ASCII.GetBytes("dummy key");
hmac.ComputeHash(Encoding.ASCII.GetBytes(macs));

// convert hash to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hmac.Hash.Length; i++)
{
sb.Append(hmac.Hash[i].ToString("X2"));
}

// the the final hash as a string
returb sb.ToString();
}



GeneralRe: Not really unique
Gratemyl
3:08 12 Apr '06  
The problem with this method is that the MACs can also be changed (quite easily, through device manager), and that a change in network hardware will invalidate the license.

The processor serial number is a really nice idea though! How do you get that? Maybe I can improve the article...

Thanks for the feedback!
GeneralRe: Not really unique
Fabrice Vergnenegre
10:55 12 Apr '06  
You're right, I wasn't expecting that changing the MAC address would be so easy!

Concerning the CPU id string, I've found this nice piece of code from By Peter A. Bromberg:

public string GetCPUId()
{
string cpuInfo = String.Empty;
string temp = String.Empty;
ManagementClass mc = new ManagementClass("Win32_Processor");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if (cpuInfo == String.Empty)
{// only return cpuInfo from first CPU
cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
}
}
return cpuInfo;
}
I've tested it and it works perfectly. His code also refers to some other hardware identifiers, like disk serial number, I think this can be of value for your licensing scheme.
GeneralRe: Not really unique
Gratemyl
13:29 12 Apr '06  
Fabrice Vergnenegre wrote:
I think this can be of value for your licensing scheme

The scheme is already implemented, but I will probably make use of this next time.

Thanks a lot for the response! I think that this will be useful to many.
GeneralRe: Not really unique
Borv
12:00 19 Oct '06  
The CPU ID + primary hard drive serial # used to work good for me. However, these guys are changing too. Want 100% security - use USB dongle.
GeneralRe: Not really unique
OneSoftware
19:51 14 Apr '07  
hi, nice idea but how can use the USB key?

Thanks you.
GeneralRe: Not really unique
OneSoftware
20:49 23 Apr '07  
hi again, we have two unique hidden serial numbers additional:
1 microprocessor, and
2 hard disk...
i believe this can be useful

GeneralRe: Not really unique
Ibrahim Dwaikat
4:57 8 Oct '08  
I think that the contents of the USB can be copied.....

My Instructor someday said to me that it is impossible to make a software uncrackable as long as it is at last converted to assembly.. and i think this is very true...

Visit Me

www.engibrahim.tk

GeneralRe: Not really unique
Gratemyl
3:07 12 Apr '06  
It is indeed, as I mentiond in "Points of Interest".

The idea was to show how the number of processors can be easily retrieved, the username, domain, and computer name, etc.
GeneralRe: Not really unique
Gratemyl
3:09 12 Apr '06  
Indeed, as I mentioned in "Points of Interest", it is not really unique.

But, as I also mentioned, it is just enough for the licensing scheme which I used.
AnswerRe: Not really unique
hk11
2:46 23 Aug '07  
Should have opted for easir yet robust mechanism...

Win32_ComputerSystemProduct.UUID ? This is unique Laugh

before c# there was darkness

GeneralRe: Not really unique
John Simmons / outlaw programmer
2:34 17 Jul '08  
hk11 wrote:
Win32_ComputerSystemProduct.UUID ? This is unique

NOT on home-made machines...

"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001

GeneralRemovable Drives
Felipe Amorim
10:44 11 Apr '06  
Does removable drives (pen drives) count as logical drives? In a more controlled environment (corporate, small business) this maybe a good alternative but when you have a global scope of users maybe the use of a username/password system allowing only one session foreach user might be a better solution.
GeneralRe: Removable Drives
Gratemyl
11:14 11 Apr '06  
Felipe Amorim wrote:
Does removable drives (pen drives) count as logical drives? In a more controlled environment (corporate, small business) this maybe a good alternative but when you have a global scope of users maybe the use of a username/password system allowing only one session foreach user might be a better solution.

The problem in my specific case was that I had to give the users the ability to run several sessions at once, while keeping the sessions to the same user.

I am not sure about removable drives, maybe somebody can find out?

The thing I wanted to show is how to generate an ID, which may also just be used for some randomness (who knows???), but also how to retrieve the username and computer name for those who may be interested.


Last Updated 11 Apr 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010