Title: Register a program for a particular system in C#
This example demonstrates a fairly simple technique that you can use to register a program for a particular system. A determined hacker could circumvent it, but for most programs it won't be worth the effort.
The idea is simple. When it starts, the program gets the system's disk drive serial number, encrypts it, and compares the result to a value stored on the computer. If the value matches, then the program is registered and starts normally.
If the value doesn't match or if no value is yet saved on the computer, then the program displays a registration form giving the user a Product ID (the disk serial number) and asking the user to email this to Customer Support. Customer Support encrypts the serial number and sends it back for the user to enter into the registration form. The form then saves that value so the program can start normally in the future.
The following code shows how the main customer program checks whether it is properly registered.
// If the program isn't registered, exit.
private void Form1_Load(object sender, EventArgs e)
// An arbitrary number to identify this program.
const UInt32 program_id = 2267918298;
if (!IsRegistered(program_id, false)) this.Close();
This code simply calls the IsRegistered method, passing it a program ID. The program ID is an arbitrary number used to differentiate among different programs that might use the same registration procedure.
The following code shows how the IsRegistered method works.
// Return true if the program is properly registered.
private bool IsRegistered(UInt32 program_id, bool default_value)
StringBuilder volume_name = new StringBuilder(1024);
StringBuilder file_system_name = new StringBuilder(1024);
UInt32 serial_number, max_component_length;
// Get the startup directory's drive letter.
// Get the drive where the program is running.
FileInfo file_info = new FileInfo(Application.StartupPath);
string drive_letter = file_info.Directory.Root.Name;
// Get the information. If we fail, return the default value.
if (!GetVolumeInformation(drive_letter, volume_name,
volume_name.Capacity, out serial_number,
out max_component_length, out file_system_flags,
// Encrypt the serial number to get the product key.
UInt32 product_key = Encrypt(program_id, serial_number);
// If this matches the saved product key,
// then the program is registered.
if (Properties.Settings.Default.ProductKey == product_key)
// It's not registered properly.
// Display the registration form.
RegistrationForm frm = new RegistrationForm();
frm.txtProductNumber.Text = serial_number.ToString();
if (frm.ShowDialog() == DialogResult.Cancel) return false;
// See if the product key matches.
UInt32 entered_key = 0;
entered_key = UInt32.Parse(frm.txtProductKey.Text);
if (entered_key == product_key)
Properties.Settings.Default.ProductKey = entered_key;
// No match. Give up.
MessageBox.Show("Incorrect product key.", "Invalid Key",
The code uses the GetVolumeInformation API function to get information about the disk containing the program's startup path. It then encrypts the disk's serial number to get a product key. If that value matches the value stored in the ProductKey program setting, the program is registered so the method returns true.
(To create the ProductKey setting at design time, open the Project menu and select Properties. On the Settings tab, create a new setting named ProductKey with type uint.)
If the encrypted serial number doesn't match the ProductKey setting, the code displays a RegistrationForm. If the user enters a product key on that form and clicks OK, the program parses the value and compares it to the encrypted serial number. If the values match, then the user has entered the correct product key, so the function saves it and returns true.
The following code shows how the Encrypt method encrypts a UInt32.
// Simple encryption and decryption.
private UInt32 Encrypt(UInt32 seed, UInt32 value)
Random rand = new Random((int)seed);
return (value ^ (UInt32)(UInt32.MaxValue * rand.NextDouble()));
This method creates a new Random object, initializing it from a seed parameter. By using the same seed (in this case, the program ID), different programs can initialize the random number generator to the same state.
The function generates a random number between 0.0 and 1.0 and multiplies it by the largest possible UInt32 value. It then uses the XOR operator to combine that value with the value it is encrypting.
(Note that this method also decrypts. If you pass it an encrypted value and the seed that was used to encrypt it, it will generate the same pseudo-random number and XOR it with the encrypted value. That restores the original value. Note also that this property isn't necessary for this program. The program never decrypts any value.)
The final piece to the puzzle is the KeyMaker program that customer support uses to generate product keys. This program simply takes an entered Program ID and Product ID (emailed to customer support by the customer). It calls the same Encrypt method, passing it the program ID (specific to the program) as the seed and the product ID (emailed by the customer) as the value to encrypt. The result is the product key, which customer support emails to the customer.
There are a few improvements you could make to this method.
- You could use stronger encryption. If you're really worried that hackers will break open your code, however, then you should consider stronger methods for verifying customers, such as making the program validate itself with a Web Service every time it runs.
- You could obfuscate the product ID so the user can't notice that it's just the disk serial number.
- You could reformat the product ID and product key so they form groups of letters instead of a simple long number.
- You could try using the serial number of the computer's CPU instead of the disk.
Download the example to experiment with it and to see additional details.