Have you ever wanted to create a licensing scheme for a .NET
application? In some ways, this process is very easy... but in others,
it's annoyingly complex. I figured I'd write about a few different
methods available, and discuss some of the issues this presents with
.NET as a platform.
First, there is no single "right way" ... it
all depends on the purpose of the licensing. For example, is the
licensing trying to reduce piracy? Or, is it in place to remind the
user or enforce licensing restrictions (such as concurrent users)? Or
a way to track support issues? Another question to consider: how
much time and effort do you want to spend on integrating a licensing
scheme? How irritating is the licensing going to be to the end user?
(I've walked away from many shareware products as their licensing
schemes were too irritating and/or limiting. And let's face it: if you're the only producer of an application, there's no market for it to begin with. So, the last thing you want to do is encourage your potential customers to use the competition.)
I'd imagine most
people would want to curb piracy and perhaps raise awareness that the application is, indeed, licensed, but as any experienced .NET developer
knows, that's not so easy. The reason is: .NET IL code is very
readable. You can try this yourself by creating a simple form or
console application and opening up the exe in Reflector or any other
similar program. This level of exposure makes it easy to discover any
proprietary algorithms in the code, or gather enough information about
the licensing scheme to circumvent it. So while licensing may help address piracy/awareness issues, it does nothing to address code protection.
Some articles on the internet
discuss some techniques for code protection, such as compiling the application using NGEN
(native image generator). I won't expound on this here, but I believe
doing this is a bad idea as it really doesn't offer any IP protection (and, it imposes a number of limitations).
There are licensing classes built into the framework, however,
these are mainly designed for design-time licensing. Here, I'm mainly
focused on runtime or end-user licensing and code protection.
Because the IL is
so readable, there are two things you can do to help protect your
application. The first: obfuscate. Code obfuscation is all about
misdirection -- an obfuscator will rename the symbols in code. In theory, it's
100% as functional as the original code, but utterly confusing to
read. This mainly helps protect your algorithms -- lookiloos will
have to be very motivated to make sense of the code. You can try this
out using Dotfuscator Community Edition, included in VS. I say that the original code is identical "in theory" because there are some gotchas to be aware of, especially with ASPX applications. When types are reflected, serialized, or otherwise loaded at runtime, you need to be careful about not renaming types that aren't fully participating in the rename. For example, you generally can't rename a page class that's specified in the "inherits" attribute of the page directive, because the type can't be found at runtime. Similarly, you need to be careful with serialization; while it's okay to serialize obfuscated code when the same code does the serialization and deserialization, be careful when compiling future versions as the type names may change (thus making the previously serialized objects non-serializable).
If you
want to go the actual licensing route, there are any number of
methods. The first thing to decide: how will activation be verified? I don't recommend activation: while it may offer great
protection, customers hate it. In addition, you're then forced to handle
authentication for as long as you have customers. The advantage, though, is clear: you remove verification code from the client, and of course can audit the activation attempts. It's effective.
The
way I recommend doing licensing: create an XML file that contains the
details you wish to record, such as customer name, date of licensing,
support expiration, or any other details you can think of. It's not
very useful to symmetrically encrypt or hash this data -- a user could
simply look at the IL to get the encryption key or determine the hash
algorithm (like SHA1) -- it's pretty standard stuff.
The best
way is to use a digital signature. Digital signatures use PKI to
create a message digest using the private key of the owner (in this
case, you). The message digest is then verified using the
corresponding public key. The purpose of a message digest is not the
encrypt the message contents, but rather ensure that the original
content has not been modified. Because it's virtually impossible to
compute a valid digest with just the public key, it's near impossible
to create a valid license without your private key. The end result:
the plain text license file can't be modified.
The tricky part
is: you still need to protect your code from modification and injection. A malicious user could simply
inject their own public key, and create a digest of their own license
file. If the application isn't secure enough, there'd be no way to
stop this. Alternatively, a hacker could simply change the IL to skip
the validation altogether. No doubt, there's a single method somewhere
that ultimately returns a boolean indicating whether or not the license
is valid: they could inject the equivalent of "return true;" to short
circuit the algorithm.
Obfuscation can certainly make this
more difficult. The next step is to sprinkle the licensing throughout the
application. You need to balance maintainability, but by not having a
single "IsLicenseValid()" method, you reduce the chances of an easy
hack.
Lastly, you need to digitally sign your code (not the license, although you do that, too) -- this is giving your assembly a strong name. You can use the strong name utility
(SN.exe) to generate a random private/public key pair. Within the
application properties, you can tell Visual Studio to sign the assembly
with this key. By doing this, the runtime will verify the application
has not been modified before allowing the application to load (same
idea as the digital signature above). (In truth, there are ways to
force the runtime to skirt these checks, but nothing is 100% foolproof.)
While
a hacker could still modify your code, they're now forced to either
skirt the .NET Framework checks, or building a new application
altogether by piecing together your IL.
The bottom line here
is: if you follow these practices, it would be a serious pain to
overcome a well protected application. There may be some people who
choose to do this anyway, but I think using obfuscation with strong named
assemblies and digitally signed license files is both time effective at development time, and effective and protecting your property at run time. In future posts, I'll blog about actually implementing these techniques.