The purpose of this article is to demonstrate a reasonably quick and effective method of license protection using the .NET framework's built-in RSA cryptography components to digitally sign and verify license terms for an application.
Almost anyone who has produced a sellable bit of software is going to be concerned (at least a little) with unauthorized distribution of their program - if only because it is often easier to pirate software than it is to legitimately buy it. This is the key when securing software for copy protection: It should be easier to buy than to pirate. The best bit of programming you can do to secure your program is to make the program actually valuable to the user, so they want to buy it. But, the next best thing is a mechanism by which you can use to correctly identify valid users of the program. There is no end to these methods, and they all have varying levels of security, administration overhead, and user-annoyance. Here are some of the common methods:
Serial Number Protection
Found on the back of a CD case, the most common copy-protection you can find often involves a long serial number that is derived from an algorithm, such that only the original key-generator can produce valid keys. Often, the first few characters define a seed entry for a pseudo-random-number generator, and the remaining characters must match the transformed output of that number generator. This has a number of drawbacks:
- It is a blanket authorization: Usernames and License-end terms, partial licenses cannot be specified
- All you need is a copy of the serial-number: there are hundreds of websites that list known serial numbers for almost any bit of commercial software.
- The user has to type in a long annoying and difficult to read serial number in order to install the software.
An extension of serial-number protection: the application contacts a central server to validate that the serial-number is one that has been issued. This allows serial numbers to be blacklisted, tracked, etc. The main drawback is that the application must have internet access in order to validate the serial number. Users will probably not be happy about this (especially if they don't have constant internet access) and you may find hackers patching your software not to illegally copy it, but just to get it to work offline. The other drawback is the amount of coding and development work that must go into a system like this that could be defeated by a proxy-server that simulates the responses of your own validation server.
License Terms File
Many systems use a license file that is supplied to the user with their purchase. The license file specifies the licensee (the user's name, address, contact details, etc.), the product being licensed, and the start and end dates of that license. The software asks for the license file on first use, copies it into a known location, then validates that the terms of the license are valid (correct software version, within the applicable date-range, etc.).
The trick here is to ensure that the license file that is supplied is authentic, if that part is covered, this is an excellent system for licensing: the license terms can be as simple or complex as you like (i.e. blanket authorization, or partial (certain features enabled)), they can have start and end dates, be restricted to particular applications or even restricted to particular users. The user isn't forced to type in a long and complex serial-number, and having the users name and contact details embedded within the terms makes it less likely that users will voluntarily share the license file.
Authenticating the License File
There is a branch of cryptography that deals with verification (or signing) of data, using Asymmetrical Encryption.
A digital signature is produced from the data being verified by generating a hash-code from the source data, and encrypting this hash with a private key.
The hash code will be unique to the source data... if even one bit in the source data changes, the resulting hashcode will be quite different. (The "strength" of any hashing algorithm is indicated by how much the hash-code changes with the smallest possible edit of the source data).
Verification of the data is achieved by using the public-part of the key to check if the digital signature (the encrypted hash) still matches the data that is being verified. If any part of the data is changed, then the digital signature will not match.
This means that you can verify that the license file the system is using definitely came from you, (the sellers of the software) without having to give the application access to the private key. If you were to use symmetrical encryption to encrypt the license-terms (so they could not be seen or changed), then the application itself would need access to the encryption key, in order to decrypt the file. The key could be found within the application EXE and extracted, allowing pirates to generate their own license files.
Using asymmetrical encryption still requires that the application have access to the public part of the key, but knowing the public part of the key will not help anyone trying to hack the system.
The danger lies in pirates being able to replace or intercept the public key with one of their own making. This would require them to get into the .exe and alter the sequence of bytes that defines the public-key (whether it is a string-literal or a resource within the EXE).
This is beyond the scope I have given myself for this article, but just a quick mention:
signing the executable itself is one potential way of trying to stop this: modification of the executable will generate a different signature, and the application can be instructed not to open. (This is how the click-once manifests work, in fact, using click-once to deploy your application makes it (almost) impossible to modify that application without regenerating the click-once manifest file.)
Using the Code
The logic for the licensing system is contained within the
General.Security namespace within the example project: this code is non-specific and portable (it doesn't reference any custom types outside the
General namespace) and the two files (Serializer.cs and RSA.cs) can be moved to any project without change. The actual implementation of the licensing system (i.e. project specific code) is contained within the
validateLicenseFile() method of the
static Program class. This provides a good example of using the methods defined in the
General.Security namespace. The
Program.validateLicenseFile() method reads the
public key out of the embedded resource (Resources.resx) and locates the license file (asking the user for the location if it can't find it). It verifies the license file signature is correct, then extracts the license-terms, de-serializes them and checks that the current software and date/time is within the terms on the license. Supplied is an example license file (valid until the year 9999) for user "
Test" (Test.lic) - The public and private key files used to generate this license are also included.
This is the
internal static bool validateLicenseFile()
License license = null;
String publicKey = Properties.Resources.publicKey;
String licenseFile = Application.LocalUserAppDataPath + "\\" +
Environment.UserName + "_user.lic";
license = License.Load(licenseFile);
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "User License Files (*.lic)|*.lic";
dlg.Title = "Select License File";
if (dlg.ShowDialog() == DialogResult.OK)
license = License.Load(licenseFile);
license = License.Load(dlg.FileName);
if (license != null)
MessageBox.Show("License File Not Supplied!", "License Check");
catch (SecurityException se)
MessageBox.Show(se.Message, "License Check");
Points of Interest
Something I discovered while doing the research for this: You can convert an array of bytes to a printable
Convert.ToBase64String(). The result
string always contains printable ASCII characters, and it is definitely not human-readable. The
LicenseTerms class for this article, (which contains the start date, end date, product name, user name, etc.) is stored in the license file (which is XML) as a
string is created by serializing the licence-terms class to a byte-array using the binary-formatter. This doesn't encrypt the license terms by any means, but it makes it much harder to read, and it also makes it easy to deal with (no special serialization is required to save a