Click here to Skip to main content
Email Password   helpLost your password?

Regarding sample application

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.

Introduction

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.

Background

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.

Strong name

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.

Flows

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.

1. Distribute keys

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.

2. Register a customer on your server side

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.

3. Client side

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.

4. Server side

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.

5. Client side again

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.

How to crack this security: Is it possible?

We can think of several ways.

1. Crack the client S/W by skipping the activation check routine?

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.

2. Copy the license file?

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.

3. Regeneration of a fraud license file?

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.

4. Disclosing the serial number and the user ID information?

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.

Using the code

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

Points of interest

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

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralEncryptAndSign and VerifyAndDecrypt
robertofrassa
22:07 14 Dec '09  
Hello,
you say looking at your code file to see the VerifyAndDecrypt() function,
but I can,t find it, neither EncryptAndSign()....

regards
GeneralCompile Error on MSVC6
cujobr
5:03 15 Jul '09  
When I compile on MS Visual C++ 6, I get several error by functions missing.

--------------------Configuration: CryptoEncryption - Win32 Debug--------------------
Compiling...
CryptoEncryptionDlg.cpp
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(155) : error C2059: syntax error : '&&' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(155) : error C2143: syntax error : missing ';' before '}' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(155) : error C2143: syntax error : missing ';' before '}' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(156) : error C2143: syntax error : missing ';' before '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(156) : error C2447: missing function header (old-style formal list?)
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(156) : error C2143: syntax error : missing ';' before ',' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(157) : error C2143: syntax error : missing ';' before '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(157) : error C2447: missing function header (old-style formal list?)
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(157) : error C2143: syntax error : missing ';' before '}' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(279) : error C2065: 'CryptBinaryToString' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(281) : error C2065: 'CRYPT_STRING_BASE64' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(303) : error C2065: 'CryptStringToBinary' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(387) : error C2039: 'GetString' : is not a member of 'CString' c:\arquivos de programas\microsoft visual studio\vc98\mfc\include\afx.h(368) : see declaration of 'CString' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(559) : error C2039: 'GetString' : is not a member of 'CString' c:\arquivos de programas\microsoft visual studio\vc98\mfc\include\afx.h(368) : see declaration of 'CString' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(781) : error C2065: 'PCCERT_CHAIN_CONTEXT' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(781) : error C2146: syntax error : missing ';' before identifier 'pChainContext1' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(781) : error C2065: 'pChainContext1' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(782) : error C2146: syntax error : missing ';' before identifier 'pChainContext2' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(782) : error C2065: 'pChainContext2' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2065: 'CERT_USAGE_MATCH' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2146: syntax error : missing ';' before identifier 'CertUsage' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2065: 'CertUsage' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2059: syntax error : '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2143: syntax error : missing ';' before '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(784) : error C2059: syntax error : '}' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2065: 'CERT_CHAIN_PARA' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2146: syntax error : missing ';' before identifier 'ChainPara' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2065: 'ChainPara' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2059: syntax error : '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2143: syntax error : missing ';' before '{' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(785) : error C2059: syntax error : '}' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(790) : error C2228: left of '.dwType' must have class/struct/union type
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(790) : error C2065: 'USAGE_MATCH_TYPE_AND' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(791) : error C2228: left of '.Usage' must have class/struct/union type
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(792) : error C2228: left of '.cbSize' must have class/struct/union type
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(793) : error C2228: left of '.RequestedUsage' must have class/struct/union type
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(800) : error C2065: 'CertGetCertificateChain' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(840) : error C2065: 'PCERT_SIMPLE_CHAIN' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(840) : error C2146: syntax error : missing ';' before identifier 'pchain' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(840) : error C2065: 'pchain' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(840) : error C2227: left of '->rgpChain' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(841) : error C2065: 'PCERT_CHAIN_ELEMENT' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(841) : error C2146: syntax error : missing ';' before identifier 'chelement' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(841) : error C2065: 'chelement' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(841) : error C2227: left of '->rgpElement' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(841) : error C2227: left of '->cElement' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(842) : error C2227: left of '->pCertContext' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(844) : error C2146: syntax error : missing ';' before identifier 'pchain2' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(844) : error C2065: 'pchain2' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(844) : error C2227: left of '->rgpChain' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(845) : error C2146: syntax error : missing ';' before identifier 'chelement2' C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(845) : error C2065: 'chelement2' : undeclared identifier
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(845) : error C2227: left of '->rgpElement' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(845) : error C2227: left of '->cElement' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(846) : error C2227: left of '->pCertContext' must point to class/struct/union
C:\Documents and Settings\Donato\Desktop\simplecertvalidateVCPP\CryptoEncryptionDlg.cpp(945) : error C2065: 'CertFreeCertificateChain' : undeclared identifier
Error executing cl.exe.

CryptoEncryption.exe - 56 error(s), 0 warning(s)

Questionhi
arminj
8:17 13 Sep '08  
hi i need your source code in c# so emergency but it doesn't have "encryp & sign" and " decrypt & verfy" functions! would you please help me?

ajgm

AnswerRe: hi
BruceWang_korea
23:31 15 Sep '08  
Dear,

I think my simplest sample already has 'Sign' and 'Verify' logic.

If you want to see how you can encrypt and decrypt a string with your Digital Certificate, please check following code.

        /// 
        /// 2006-08-19 brucewang
///
byte[] DecryptMessage(string strEncrypted, X509Certificate2 cert)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

try {
string strxmlkeystring = cert.PrivateKey.ToXmlString(true);
RSA.FromXmlString(strxmlkeystring);

//char[] charstream = strEncrypted.ToCharArray();
//byte[] bytestream = new byte[charstream.Length];
//for (int i = 0; i < charstream.Length; i++)
//{
// bytestream[i] = (byte)charstream[i];
//}
byte[] bytestream = Convert.FromBase64String(strEncrypted);

return RSA.Decrypt(bytestream, false);
}
catch {
}
finally {
if (RSA != null)
{
RSA.Clear();
}
}

return null;
}


byte[] DecryptMessage(string strEncrypted, string certXml)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

try {
RSA.FromXmlString(certXml);

//char[] charstream = strEncrypted.ToCharArray();
//byte[] bytestream = new byte[charstream.Length];
//for (int i = 0; i < charstream.Length; i++)
//{
// bytestream[i] = (byte)charstream[i];
//}
byte[] bytestream = Convert.FromBase64String(strEncrypted);

return RSA.Decrypt(bytestream, false);
}
catch {
}
finally {
if (RSA != null)
{
RSA.Clear();
}
}

return null;
}

And if you want to see the 'Symmetric encryption/decryption', not the Asymmetric one like above, please check this. I remember this code was in the MSDN sample. I think I just copied that one with little change. This one shows how you can use AES block cipher to do encryption/decryption.

        /// 
        /// 2006-08-19 brucewang
///
byte[] encryptStringToBytes_AES(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");

// Declare the streams used
// to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt = null;
CryptoStream csEncrypt = null;
StreamWriter swEncrypt = null;

// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;

// Declare the bytes used to hold the
// encrypted data.
byte[] encrypted = null;

try {
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;

// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

// Create the streams used for encryption.
msEncrypt = new MemoryStream();
csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
swEncrypt = new StreamWriter(csEncrypt);

//Write all data to the stream.
swEncrypt.Write(plainText);

}
finally {
// Clean things up.
// Close the streams.
if (swEncrypt != null)
swEncrypt.Close();
if (csEncrypt != null)
csEncrypt.Close();
if (msEncrypt != null)
msEncrypt.Close();

// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}

// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();

}

/// /// 2006-08-19 brucewang
///
string decryptStringFromBytes_AES(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");

// TDeclare the streams used
// to decrypt to an in memory
// array of bytes.
MemoryStream msDecrypt = null;
CryptoStream csDecrypt = null;
StreamReader srDecrypt = null;

// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;

// Declare the string used to hold
// the decrypted text.
string plaintext = null;

try {
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;

// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

// Create the streams used for decryption.
msDecrypt = new MemoryStream(cipherText);
csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
srDecrypt = new StreamReader(csDecrypt);

// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
finally {
// Clean things up.
// Close the streams.
if (srDecrypt != null)
srDecrypt.Close();
if (csDecrypt != null)
csDecrypt.Close();
if (msDecrypt != null)
msDecrypt.Close();

// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}

return plaintext;

}

HTH.

Happy programming.

--------------------------------------------------
$yes 4 8 15 16 23 42
Generalhosts
V. Svetoslavov
3:39 9 May '08  
Assume the url to refer is http://www.myserver.com/register
Whether a web service or some other routine, we rely to find OUR server on the specified url.
Windows XP provides users with hosts file (or lmhosts? don't remember at the moment), so the url to find can be changed to point to any ip address. In combination with any msil decompiler, all this registration validation would be easy to break, I think. Are there any common approaches in that direction?
GeneralRe: hosts
BruceWang_korea
19:51 9 May '08  
You mean kind of spoofing and other network hacking attempts like redirect the connection to some other server than the actual one?

Even though you redirect the connection, the server and client should have matching Assymetric key pair. If your new fake server don't have proper key pair, you can not do any further..

HTH
GeneralRe: hosts
icetea94
6:52 6 Feb '10  
But you can remove strong name and replace server's public key in the assembly file with your own public key.

Cheers,
icetea
GeneralDistributing keys
mjmim
2:34 6 Mar '08  
This is one of the best guidelines i have seen for tightly defending a program from theft!
I understood the entire concept but do not understand how to implement the beginning - distributing keys.
Any chance you could help me out? Or point me out to a place that explains how to setup such a system in greater detail (for a dummy like me).
Michael Mimouni
mjmimmm@hotmail.com
AnswerRe: Distributing keys
BruceWang_korea
18:42 6 Mar '08  
Dear,

I think you can find 'complete' information you want to know here.
http://www.faqs.org/rfcs/rfc2409.html[^]

That is the fundamental, but maybe too complex.

Actually there can be many many ways for distributing the keys.
Mine was just one example. You generate the private/public key pair of the server, and distribute the public key (digital certificate), generate an encryption key for the block cypher, and encrypt that key with the server's public key, and send it to the server. Then the server can decrypt the information with its own private key, and know the encryption key for the block cypher.

If you read that RFC document and think about it carefully, then you will know many things...

I hope this helps..

Best regards.,
Questionusing makecert
johanm_2
3:32 31 Jul '07  
Hi, nice article. Can you provide some examples on how to use makecert to effectively create certificates to use with your example code?
AnswerRe: using makecert
BruceWang_korea
20:46 31 Jul '07  
Hello,

You can do this.. to create private key and the certificate

makecert.exe -sv a.pvk a.cer
And you can make *.spc file like this.

cert2spc.exe a.cer a.spc
Finally, you can make *.pfx file like this

pvk2pfx -pvk a.pvk -spc a.spc

makecert.exe just creates the 'test purpose' certificates, so you better install the 'Certificate server' on your Window2k or 2003 server, and enroll your own certificate.

Best regards.
GeneralPossible Crack: remove the strong name and hack the client
MartinSchmidt
23:47 18 Jun '07  
Hi,
this is a very nice idea. However - any software protection mechanism that relies on strong naming faces a big problem. It's very easy to remove the strong name. See http://www.codeproject.com/dotnet/StrongNameRemove20.asp for example. So hacking the client becomes an option again.

Regards,
Martin
GeneralRe: Possible Crack: remove the strong name and hack the client
BruceWang_korea
23:53 18 Jun '07  
Dear Martin,

Yes, another friend also pointed out Blush

I hope somebody post a new article about encrypting or obfuscating the assembly file itself.

Cheers!
GeneralProblem with implementing this solution
Goran _
5:33 13 Jun '07  
Hello, I don't have much experience with strong names and their implementation. I have created private and public keys using makecert and pvk2pfx, but what does mean register (enroll) them on server. When I use them in EncryptAndSign, function returns empty string. Can you make some example how to implement your solution, or to point me to some direction. Example, lines how you create keys (what switches do you use), and what keys (where private and where public) do you pass to functions. My goal is to implement your solution using email registration, if possible.

Thanks,
Goran
AnswerRe: Problem with implementing this solution
BruceWang_korea
15:21 13 Jun '07  
Dear Goran,

'Registration of the certificate' means, if you have made *.pfx file, you can register the certificate inside it (even the private key information) by double clicking that *.pfx file. Then it will be registered on the server's 'Certificate Storage'.

You can find the 'registered' certificates from the IE's 'tools' -> 'option' menu, and.. -> 'Contents' -> 'Certificates' item. And you can refer to those certificates with some APIS....

I think I better upload my 'very simple' application explaining the basic operations for that... Sooon I will update this article..


And in the function 'EncryptAndSign', you just pass the 'string' you need to encrypt. of course you need to feed proper private key information. In this article I just told that you also can use 'xml string' for private key information... Implementation is up to you...

HTH, and I hope my new simple application sample give you more hint...

Best regards.
GeneralRe: Problem with implementing this solution
Goran _
0:24 14 Jun '07  
Thanks, Bruce, looking forward to see your example. Simple or not, it will surely clear some things.

Thanks again,
Goran
QuestionUsing just one key pair isn't good enough?
barbq2000
10:10 26 Mar '07  
I've read the article a few times now, and learned my gaps of PKI.
So now I'm asking a naive question to understand what I'm missing. I can't see why do I need to give a private key to the user.
The following scenraio assumes a Strong named software, and a key pair whereas the private key lies in the server.

When the user registers the software, his CPU ID and USER NAME are hashed into a SERIAL number then encrypted with the private key of the server to make a Signature.

On-Way-Hash(Name + CPU ID) = Serial

PrivateKeyEncription(Serial) = Signature

A License file includes the USER NAME, and the above Signature.

Verification: When the license is (in the software) his USER NAME (from the license) and CPU ID (taken from his computer in real time) is hashed into a serial number. This serial number is then compared against the decrypted serial from the signature. The public key will be embedded within the application

* The license cannot be regenerated because it needs the private key of the server
* The license is not portable because the CPU ID is taken in real time
* The application cannot be modified because it is strong named
* The application cannot be reactivated because the server holds the records

I've used only one key pair, and I haven't enrolled any certificate.
What have I missed?

AnswerRe: Using just one key pair isn't good enough?
BruceWang_korea
15:16 26 Mar '07  
Thank you for good question. Wink

In my model, the client verifies the 'S/W activation' status by checking the 'encrypted' server reply. The client should 'decrypt' that message. In that case, you can use 'private key' only, with .NET 2.0 crypto library. We can not distribute Server's private key, so in this model, I embedded the private key information in the application itself, for prohibiting the fraud of certificate.

If you change the model to get confirmation from server every time you start the application, maybe we don't need the client's private key information. We just need to verify the server only in that case. Instead of wasting Internet connection resources, I chose to the check the local file which is encrypted.

So, major point I couldn't implement your idea was because of the limitation of crypto library.
Your question is very similar to my question below. And I think mr Zambo's answer is perfect.
GeneralExcellent Solution
vangarapav
16:13 11 Oct '06  
Nice posting and very useful thought. Cool

Pavan
General<--- This is my friend implemented the WebService
BruceWang_korea
16:17 11 Oct '06  
Hello,

I didn't post your web service related codes
I hope I can see your posts soon here.

Thanx buddy.Roll eyes
GeneralGood Solutions But what to protect whole source in .Net ?
VBNewComerVBDOTNET
5:56 11 Oct '06  
Hi,

This is good example and workable too., But in .NET, there is IL, thru which anybody can get whole source by decompile it and then there is no meaning to all such strick protection and activication procedures as your source is gone !!!

Does anybody has solution for that so that our code itself is protected instead of software !!! Is there any method which we can implement to protect our intellectual property or say source code protection against decompilation ? Please let me know if there is something available for or code example to do that.

Thanks
GeneralRe: Good Solutions But what to protect whole source in .Net ? [modified]
BruceWang_korea
15:02 11 Oct '06  
Hello, Smile

If you're worrying about reverse engineering, basically languages like C# (or any other languages which can produce .NetFramework executables), Java is not a good idea.

Even though you use traditional compiled languages like C, C++, Pascal, etc, you or any skillful assembly programmers can read the machine codes. Yes, it is very difficult to understand just from the mahcine code, but you can copy and paste some part of the code, or you can skip some function (like S/W validation).

In these days, there are a lot of professional tools like SoftIce, OllyDebugger, etc... These eases the pain of reading/modifying the machine (binary) codes..

If you really want to stop someone to steal or peek your even binary code, then there maybe some ways (maybe you can hook or monitor the OS' debugging mode, or twist your assembly code, etc...), but also they are not perfect.

If you really think your code is valuable and superb, and you want to keep it, I recommend you just apply the patent for your logics of that code.

Smile


-- modified at 20:10 Wednesday 11th October, 2006
GeneralRe: Good Solutions But what to protect whole source in .Net ?
VBNewComerVBDOTNET
17:39 11 Oct '06  
Thanks Bruce for very informative answer, but what I was expecting in answer is, I need some good and free utility which removes CLI Header from our .Net compiled executive files so that it is not applicable to reverse engineer thru simple mechanism and makes it difficult to read thru assembly.

Yes, my code may not be such a valuable so that I need 1000% protection but it gives logic as so good softwares are available that even from our compiled output, it creates projects of VB.NET or VC.NET and that way, we becomes helpless to deploy any copy protection mechanism to protect our application. We can protect our application from being copied but not from being re-generated with everything changed in it thru original source codes they can get !!!

Please let me know if any good method to remove CLI header from our application is available.
GeneralRe: Good Solutions But what to protect whole source in .Net ?
BruceWang_korea
17:56 11 Oct '06  
Hello, after reading 'Jeremy L-T' I found I was somewhat careless.

But Hey, I belive all our codes are VALUABLE. Because we devoted.

By the way, I actually had no idea there was a tool what 'Jeremy L-T' suggested.

There can be ways to put lots of check routines in your final .Net Assembly but protecting your code itself from it... hmm.. I think there is only way what Jeremy suggested.

SorryFrown
NewsRe: Good Solutions But what to protect whole source in .Net ?
Jeremy L-T
17:27 11 Oct '06  
VBNewComerVBDOTNET wrote:
But in .NET, there is IL, thru which anybody can get whole source by decompile it and then there is no meaning to all such strick protection and activication procedures as your source is gone !!!

You can go one step beyond obfuscating, this is what I mean:

protector

http://www.remotesoft.com/salamander/protector.html[^]

Cheers,
JT


Last Updated 18 Jun 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010