使用RSA加密保护你的.net应用程序(winform)

原文地址:http://www.codeproject.com/Articles/203840/RSA-License-Protection

介绍

本文的目的是通过使用.net内置的RSA加密组件对winform程序进行签名并校验,以达到有效保护winform程序的目的。

背景

所有应用程序开发者,尤其是哪些程序需要销售并且得到付款的开发者,都或多或少的关注程序的未授权的分发问题。因为无论什么时候,程序都存在被破解(或反编译)的可能性,总有人不想花钱去买你的软件。而你需要明白的一个事实是,无论你如何加密软件,软件都存在被破解和反编译的事实,因此,加密的目的,并不是为了防止反编译或者破解,而是加大破解的难度,让大多数人觉得购买你的软件,比寻找破解版更加容易。并且最重要的一点,就是程序员需要花费更多的精力,去完善软件,提升服务,尤其是配套的,售后的服务质量,让顾客觉得心甘情愿购买你的软件,或者更确切的,是购买你的服务!然后,再回头来讨论下这篇文章的最初目的,就是你的程序,应当,也应该校验用户的合法性,毕竟你是需要赚钱的! 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:

  1. It is a blanket authorization: Usernames and License-end terms, partial licenses cannot be specified
  2. 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.
  3. The user has to type in a long annoying and difficult to read serial number in order to install the software.

Online Activation

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.)

如何使用代码

The logic for the licensing system is contained within the General.Securitynamespace within the example project: this code is non-specific and portable (it doesn’t reference any custom types outside the Generalnamespace) 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 thevalidateLicenseFile()method of the static Program class. This provides a good example of using the methods defined in the General.Securitynamespace. The Program.validateLicenseFile()method reads the publickey 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 validateLicensemethod:

/// validate the user-license.
internal static bool validateLicenseFile()
{
    try
    {
        // reserve a license object:
        License license = null;

        // get the public key from internal resource:
        String publicKey = Properties.Resources.publicKey;

        // work out the expected license file-name:
        String licenseFile = Application.LocalUserAppDataPath + "\\" + 
                    Environment.UserName + "_user.lic";

        // does the license file exist?
        if (File.Exists(licenseFile))
        {
            // load the license:
            license = License.Load(licenseFile);
        }
        else
        {
            // prompt the user for a license file:
            OpenFileDialog dlg = new OpenFileDialog();

            // setup a dialog;
            dlg.Filter = "User License Files (*.lic)|*.lic";
            dlg.Title = "Select License File";

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    // copy the license file into the local app data directory:
                    File.Copy(dlg.FileName, licenseFile);

                    // if it made it here, load it:
                    if (File.Exists(licenseFile))
                    {
                        license = License.Load(licenseFile);
                    }
                }
                catch
                {
                    // can't copy the file?.. load the original:
                    license = License.Load(dlg.FileName);
                }
            }
        }
        if (license != null)
        {
            // validate the signature on the license with the message contents, 
            // and the public key:
            LicenseAuthorization.ValidateLicense(license, publicKey);

            // if we get here, the license is valid;
            return true;
        }
        else
        {
            // no license file...
            MessageBox.Show("License File Not Supplied!", "License Check");
            return false;
        }
    }
    catch (SecurityException se)
    {
        // display the reason for the license check failure:
        MessageBox.Show(se.Message, "License Check");
    }

    // return false...invalid license.
    return false;
    }
}

Points of Interest

Something I discovered while doing the research for this: You can convert an array of bytes to a printable stringusing Convert.ToBase64String(). The result stringalways contains printable ASCII characters, and it is definitely not human-readable. The LicenseTermsclass 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. The stringis 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 string).

更新版本

  • Version 1.0

文章授权

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

分享到