views:

2131

answers:

9

For a web application, I would like to create a simple but effective licensing system. In C#, this is a little difficult, since my decryption method could be viewed by anyone with Reflector installed.

What are some methods for encrypting files in C# that are fairly tamper-proof?

A: 

Why would you need to encrypt it? If it is tampering you are afraid (for instance someone increasing the number of users), could you perhaps just sign it with e.g. your organization's digital certificate instead?

Eyvind
A: 

or perhaps you want to tie a license to a particular machine, for example by generating the license key based on the md5 value of the c drive of the pc. then your license would be machine specific.

melaos
You mean the serial number of the C drive right? not the contents. And make sure you dont use the volume ID, that can easily be copied.
Martin
Hashing the drive is a bad idea - way too much data. Better to use DPAPI (machine-scope) to protect a GUID (only create if it doesn't already exist) as a "machine ID". Use the user-scope DPAPI to protect a "user ID".
devstuff
+1  A: 

The only way to provide some sort of security with licensing is to force an online login against credentials hold by yourself (really abstract spoken).

All other methods take more time, and therefore more money, to implement, as to crack and abuse your software instead of buying a license.

.NET has some good cryptographic classes, but as you mentioned, if you are coding the en-/decription of the license, everyone can decompile it easily.

BeowulfOF
+4  A: 

Use a signed XML file. Sign it with the private key part of a keypair and check it with the public key part in your software. This gives you the oppertunity to check whether the license has been altered and also to check if the license file is valid.

Signing and checking of a signed XML file is documented in the MSDN.

It's of course logical that you sign the license file at your own company and send the license file to the customer who then places the license file in a folder for you to read.

Of course, people can cut out/hack your distributed assembly and rip out the xml sign checking, but then again, they will always be able to do so, no matter what you do.

Frans Bouma
I found the MSDN materials you were talking about and XML signing looks simple enough. However, could you explain some more about the public/private key part of your answer? I didn't see anything about using two different keys to sign and verify an XML file.
David Brown
use 'sn -k' to produce a key pair. This key pair is used to sign the XML. To check the XML, you use the public part. Do not include the whole key in your code. Use sn -p to extract the public part to a file. Use that in your code you distribute to verify the license.
Frans Bouma
+15  A: 

It sounds like you want to be using Public/Private cryptography to sign a license token (an XML Fragment or file for example) so you can detect tampering. The simplest way to handle it is to do the following steps:

1) Generate a keypair for your company. You can do this in the Visual Studio command line using the SN tool. Syntax is:

sn -k c:\keypair.snk

2) Use the keypair to strongly name (i.e. sign) your client application. You can set this using the signing tab in the properties page on your application

3) Create a license for your client, this should be an XML document and sign it using your Private key. This involves simply computing a digital signature and steps to accomplish it can be found at:

http://msdn.microsoft.com/en-us/library/ms229745.aspx

4) On the client, when checking the license you load the XmlDocument and use your Public key to verify the signature to prove the license has not been tampered with. Details on how to do this can be found at:

http://msdn.microsoft.com/en-us/library/ms229950.aspx

To get around key distribution (i.e. ensuring your client is using the correct public key) you can actually pull the public key from the signed assembly itself. Tgus ensuring you dont have another key to distribute and even if someone tampers with the assembly the .net framework will die with a security exception because the strong name will no longer match the assembly itself.

To pull the public key from the client assembly you want to use code similar to:

 /// <summary>
 /// Retrieves an RSA public key from a signed assembly
 /// </summary>
 /// <param name="assembly">Signed assembly to retrieve the key from</param>
 /// <returns>RSA Crypto Service Provider initialised with the key from the assembly</returns>
 public static RSACryptoServiceProvider GetPublicKeyFromAssembly(Assembly assembly)
 {
  if (assembly == null)
   throw new ArgumentNullException("assembly", "Assembly may not be null");

  byte[] pubkey = assembly.GetName().GetPublicKey();
  if (pubkey.Length == 0)
   throw new ArgumentException("No public key in assembly.");

  RSAParameters rsaParams = EncryptionUtils.GetRSAParameters(pubkey);
  RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
  rsa.ImportParameters(rsaParams);

  return rsa;
 }

I've uploaded a class with a bunch of crypto methods that you may find useful at:

http://www.chaosink.co.uk/files/code/encryptionutils.zip

to help get you on your way.

Wolfwyrd
Exactly what I said below.
Frans Bouma
Extremely helpful encryption code. Thank you Wolfwyrd for making it available.
Matt
@Wolfwyrd : The utils that you've made available, could you provide some brief instructions on how to use them?
Ian
A: 

RE: Do not include the whole key in your code. Use sn -p to extract the public part to a file. Use that in your code you distribute to verify the license.

Using the code from the MSDN articles, I created a small app (LicMaker) to facilitate this. The app is signed with the full key pair. The input is an unsigned .XML license file. The output is the original XML + signature in a signed .LIC file. My product is also signed by the same full key pair file as well. The product verifies the .LIC file has not been tampered with. Works Great. I did not use sn -p for this solution.

Mark
This isn't the appropriate place to ask your own question. Use the "Ask Question" at the top, or add a reply directly to the answer you're referring to.
David Brown
A: 

It appears that the MSDN Samples use default parameters and this results in machine- specific signing such that you cannot sign on one machine and verify on an arbitrary other machine. I modified the logic in those samples to use the keys in my snk on the signing machine and keys from my assembly in the verifying machine. This was done using the logic from the MSDN sample and routines in the (very nicely done) ExcryptionUtils.cs sample linked above.

For signing the file, I used this:

RSACryptoServiceProvider rsaKey = EncryptionUtils.GetRSAFromSnkFile(keyFilePath);

For verifying the file, I used this:

RSACryptoServiceProvider rsaKey = EncryptionUtils.GetPublicKeyFromAssembly
   (System.Reflection.Assembly.GetExecutingAssembly());

BTW: I observed that the XML signature verification ignores XML comments.

mark
+1  A: 

Wolfwyrd's answer is excellent, but I just wanted to offer a muich simpler version of Wolfwyrd's GetPublicKeyFromAssembly method (which uses a lot of helper methods from the library Wolfwyrd graciously supplied).

In this version, this is all the code you need:

/// <summary>
/// Extracts an RSA public key from a signed assembly
/// </summary>
/// <param name="assembly">Signed assembly to extract the key from</param>
/// <returns>RSA Crypto Service Provider initialised with the public key from the assembly</returns>
private RSACryptoServiceProvider GetPublicKeyFromAssembly(Assembly assembly)
{
    // Extract public key - note that public key stored in assembly has an extra 3 headers
    // (12 bytes) at the front that are not part of a CAPI public key blob, so they must be removed
    byte[] rawPublicKeyData = assembly.GetName().GetPublicKey();

    int extraHeadersLen = 12;
    int bytesToRead = rawPublicKeyData.Length - extraHeadersLen;
    byte[] publicKeyData = new byte[bytesToRead];
    Buffer.BlockCopy(rawPublicKeyData, extraHeadersLen, publicKeyData, 0, bytesToRead);

    RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
    publicKey.ImportCspBlob(publicKeyData);

    return publicKey;
}
Cocowalla
A: 

When you implement your signing / verification code, be careful not to put it in a separate assembly. Code Access Security tamper-proofing is now very easy to bypass, as explained in the article CAS Tamper-Proofing is Broken: Consequences for Software Licensing.

Obligatory disclaimer & plug: the company I co-founded produces the OffByZero Cobalt licensing solution.

Duncan Bayne