![]() |
General Programming »
Cryptography & Security »
Security
Intermediate
License: The Code Project Open License (CPOL)
A simple but robust software protection and activationBy BruceWang_koreaA simple but robust software protection and activation. |
C#, Windows, .NET 2.0VS2005, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
In the original article, I posted a code snippet of "encrypt and sign" and "decrypt and validate." However, this was not enough to solve our friends' difficulties. Because of a lack of time, I couldn't make a fully working server and client test application. Nor could I even cut and paste some code from my current products. Instead I decided to post my old application, which was just for my learning purposes. It turned out to be very simple, so I think my new application samples will give you a clearer understanding. My new samples enable you to select a certificate from your machine's certificate storage, show the information of the selected certificate, encrypt a given string with the certificate and then decrypt the encrypted string.
Protecting desktop applications has been a kind of "hide and seek" game so far. Even though you twist and add protection methods, crackers may still find a "jump" command from your compiled binary and change it to "nothing." As a result, protection techniques have become more painful and much too sophisticated. The situation became somewhat different for programmers when Microsoft's .NET Framework came out. If we can utilize the new security infrastructures that the .NET Framework provides, we can protect our desktop applications very, very easily. You don't need to twist your final assembly codes or install special device drivers or kernel level hooks.
The security level of the techniques in this article is not very high, but it is good enough I think. I summarize this issue as a conclusion at the end of this article. We can still discuss and find better solutions, I hope. I didn't attach any proper "executable binaries" for instant testing because I do not believe it is that difficult for you to just copy and paste my code to where you want it. If you read my article once, then you should have no problems using my code. I hope you understand that this is just a conceptual introduction.
For your ease of understanding, it will be better if you know some basic concepts of PKI and "Strong names" in the .NET Framework. I also have a basic understanding of PKI, but not a full mathematical understanding of how RSA works. So, I previously asked some stupid questions like, "Can I decrypt a message with a public key instead of a private key? I don't mean 'Sign'..." At that time, everybody asked me in turn, "Why do you want to do that?" and "You just sign/verify, man."
However, I wanted to know whether it was possible in C# or mathematically impossible. That's OK, though. You don't have to worry too much. ;) I will try to explain briefly and plainly.
You can think of "Strong names" in the .NET Framework as being a little bit similar to "Authenticode." It is signing your assembly files. Once your main .NET Framework application is signed, the loader of the application -- i.e. the .NET Framework -- will check file integrity. So if a hacker changes code in that file, the application loader will just show an error and will not start the application. You can check it by building with Strong name and then editing the code area of your application.
One thing you need to know is that if your main application refers to other assemblies, then those other assemblies need to have Strong names as well. Maybe some of you will feel overly constrained about this restriction. However, it is not actually a restriction and is quite reasonable.
This scenario requires a server and a client. The server can be a web service or your own network service. The client here is a desktop application.
First of all, you need to issue two certificates either from the "Certificate service" of Windows 2003 Server or by using MakeCert.exe from the .NET Framework SDK. One is for the server and the other is for the client. Then you enroll and you also need to make a private key. I prefer making those certificates as *.pfx files because loading private key information from a *.pvk file seems very difficult in C# right now. For more information, please refer to MSDN.
Now in your server side, please register the two certificates. The server should be able to use its own private key and should use only the public key of the client. Distribute the server's certificates with only the public key (i file) and the client's private key.
You can make your own customer DB containing the customer's ID and a serial number. When a customer buys your software, register that person to your database.
Now when your application starts, send the "S/W activation request" to the server. Maybe your application shows the "Registration Form" asking for the serial number and user ID information. To make this session safe, perhaps you can use a web service with HTTPS. When you send the request, you can specify your hardware information, like CPU ID, etc. Note: For getting the CPU ID information, please refer to Get System Info using C# from Nitin Kunte. I didn't attach his code here in my document; I just added my Sign/Verify/Enc/Dec code.
If the serial number and user information is found on your "Registered customers" DB and the person is "Not validated yet," then make a 'Validation confirmed' message. In it, please specify the "Hardware information" from that user. ;) That's my tricky point here. Finally, encrypt that message with the user's public key and sign it with the server's private key. Send the encrypted and signed message back to the client.
Now that the client has received the server's reply, verify the message with the server's public key. Then decrypt the original message from the server with the client's private key. Please see the "Points of interest" section of this article for more details.
If you decrypt the message from the server side, you will find the CPU ID that you have just sent, plus any "validation confirmed" messages (any string you decided). You should check the two pieces of information. If everything is correct, then save the message to your local folder and run the application. From now on, whenever you start the application you load that message file -- this is the license file -- and check the validity, i.e. verify, decrypt the message, check validation message and check CPU ID.
We can think of several ways.
THe client software uses Strong name. The software is signed, so if any hacker changes the code of the software, the integrity (hashed value) of the software itself changes. The loader of the client software is the .NET Framework. The .NET Framework checks the integrity of loading the software. If it is changed, the .NET Framework fails to load the software. So if the hacker wants to hack the client software, he must also crack the .NET Framework, which is tightly bound to the Operating System. This is extremely difficult.
The CPU ID number is written and encrypted in the license file. The client software checks the CPU ID number written in the license file. If the number written in the license file and the number that the client software found on the fly when it was executed are different, then the client software closes itself. So if user A sends his license file to user B who is using a different computer, the client software won't run because of the CPU ID difference. To skip this procedure, the hacker must crack the software and the .NET Framework, which is extremely difficult.
To make fake a license file, the hacker must have the server's private key. However, the server's private key resides only on the server. So there must be one spy in the server area to steal the server's private key. This is not a technical problem, but a so-called "Social Engineering" problem. Therefore we can neglect this method because we are talking only about the technological viewpoint here. The client software uses the server's public key inside the code. So, if the hacker uses his own private key, he also must change the client software, which is extremely difficult.
If anyone reveals the serial number and user ID, then the person receiving that information can type it in when the client software asks the user for it. However, if the user ID and serial number are already "Activated," then the server denies reactivation and replies to the client that it is already activated. So, the client fails to continue. To activate it again, the user should call the vendor to enable software reactivation. User identification should be done on the phone.
You need to refer the following namespaces:
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.IO;
You will use EncryptAndSign and VerifyAndDecrypt. These functions use the "AES encryption/decryption" routine, which is from the MSDN sample. Please refer to my code attachment for more details.
public string EncryptAndSign(string strOriginalMessage,
X509Certificate2 sender, X509Certificate2 receiver)
{
...
}
public string VerifyAndDecrypt(string strOriginalMessage,
X509Certificate2 sender,
X509Certificate2 receiver, ref bool Verified)
{
...
}
If you look at my code file, you can see the VerifyAndDecrypt() function. Currently, it shows the Certificate list dialog and lets you select one of the options. However, you can easily change the function to find a certificate without showing any dialog and return the certificate by referring the Subject Name of the Certificate. I think this is easy for you. Perhaps on your server side, you can use my code like this:
// Server's certificate
X509Certificate2 Cert_sender = SelectCertFromTheStore();
// Client's
X509Certificate2 Cert_receiver = new X509Certificate2(@"c:\receiver.cer");
string strResult = EncryptAndSign("hello world!!! 30 Days trial, " +
"CPU ID = asdfasdf, SerialNumber",
Cert_sender, Cert_receiver);
One thing I need to tell you is that when you use the client's private key and the server's public key in the client side, I recommend that you to use the data directly from your source code -- i.e. hard-coded -- and not from a certificate file or from certificate storage. This is because when you load them from the file, you need to be very careful about the possibility of a hacker making a different certificate and overwriting them on the client machine. With that, they can cheat your application. Even if you load them from "Certificate storage," which is very secure, my code for selecting the certificate from Certificate storage via the Subject Name of the certificate is not so safe. If you can provide safer code for selecting the certificate, I will appreciate it very much.
I and my friend faced some problems on the server side when my friend implemented the web service on a Windows 2003 server. There was no problem selecting a certificate from Certificate storage on a Windows 2000 server, but when we moved the web service to Windows 2003, it always failed to load the certificate from Certificate storage. We tried "Impersonation" and then applied some solutions for "Setting access rights to the Certificates on the machine" that we found here on CodeProject.com, but we still couldn't solve it. So, we used the FromXmlString() function of RSACryptoServiceProvider and used the private key data in XML format. (Hard-coded -o-;;;)
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 18 Jun 2007 Editor: Genevieve Sovereign |
Copyright 2006 by BruceWang_korea Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |