views:

307

answers:

4

I have an issue where I need to be able to have a compiled exe ( .net 3.5 c# ) that I will make copies of to distribute that will need to change a key for example before the exe is sent out.

I cannot compile each time a new exe is needed. This is a thin client that will be used as part of a registration process.

Is it possible to add a entry to a resource file with a blank value then when a request comes in have another application grab the blank default thin client, copy it, populate the blank value with the data needed.

If yes how? If no do you have any ideas? I have been scratching my head for a few days now and the limitation as due to the boundaries I am required to work in.

The other idea I has was to inject the value into a method, which I have no idea how I would even attempt that.

Thanks.

A: 

What comes to my mind, but not tried yet: Create a default String in your program, for example as

static public string regGuid = "yourguidhere";

Then, search the compiled EXE with any decent hex editor. If you find the string, replace it with another test. If you still can execute the program, you could try to automate this process and voila! Here you are.

Marcel
I don't know if unsigned assemblies have any sort of checksum, but I think that will fail for signed assemblies, as the signature will no longer be valid.
Jon Skeet
Not to mention all the fun if the length changes...
Marc Gravell
That is the problem, the key cannot be guaranteed to be the same length every time. Hence a variable length key.
nitefrog
@nitefrong: As long as you allocate *more* space than the key will need (e.g. 40 characters and just pad the key out with spaces when you insert it), that isn't an issue. But signing will be.
Jason Williams
+1  A: 

From within the capability of the .NET code itself, I'm not sure if this is doable. But it is possible to dynamically generate a .NET DLL which contains some key that can be referred from the main application. That is, if you wouldn't mind a second file in the distribution.

Or if you don't mind to use Ildasm to disassemble the .exe, change the key, then use Ilasm to reassemble, then you can do something to automate that.

Amry
If you convert to il and convert back will that cause any issues with signing the exe once it is converted back. The process, needs to be automated.For the life of me I cannot get UpdateResource to work. It modifies the file but does not overwrite the Name value...
nitefrog
You can sign the exe when reassembling it using the ilasm /key=keyfile argument.
Amry
I just tried and created an IL, the issue is that the resource file info does not show anywhere. I can see the name but not the value.Has anyone gotten UpdateResource to work?
nitefrog
+1  A: 

Convert the assembly to IL, do a textual search and replace, recompile the IL to an assembly again. Use the standard tools from the .NET SDK.

Daniel Earwicker
Dan and Amry I just posted this question:http://stackoverflow.com/questions/2749113/compile-il-code-at-runtime-using-net-3-5-and-c-from-fileI am going to take the ildasm and ilasm approach, but do you know how to automate this without having to command line it using process.start?
nitefrog
+4  A: 

Instead of embedding the key in the assembly, put it in the app.config file (or another file delivered with the application) and prevent your application from running if the key is not present and valid. To protect it against modification by users, also add an RSA signature the config file.

This code could be used to generate XML containing your key.

public static void Main()
{
   Console.WriteLine(GenerateKey());
}

public static Byte[] Transform(Byte[] bytes, ICryptoTransform xform)
{
   using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
   {
      using (CryptoStream cstream = new CryptoStream(stream, xform, CryptoStreamMode.Write))
      {
         cstream.Write(bytes, 0, bytes.Length);
         cstream.Close();
         stream.Close();
         return stream.ToArray();
      }
   }
}

public static string GenerateKey()
{
   RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
   // This is the private key and should never be shared.
   // Generate your own with RSA.Create().ToXmlString(true).
   String rsaPrivateKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent><P>4SMSdNcOP0qAIoT2qzODgyl5yu9RubpIU3sSqky+85ZqJHXLUDjlgqAZvT71ROexJ4tMfMOgSWezHQwKWpz3sw==</P><Q>0krr7cmorhWgwCDG8jmzLMo2jafAy6tQout+1hU0bBKAQaPTGGogPB3hTnFIr84kHcRalCksI6jk4Xx/hiw+sw==</Q><DP>DtR9mb60zIx+xkdV7E8XYaNwx2JeUsqniwA3aYpmpasJ0N8FhoJI9ALRzzp/c4uDiuRNJIbKXyt6i/ZIFFH0qw==</DP><DQ>mGCxlBwLnhkN4ind/qbQriPYY8yqZuo8A9Ggln/G/IhrZyTOUWKU+Pqtx6lOghVdFjSxbapn0W8QalNMFGz7AQ==</DQ><InverseQ>WDYfqefukDvMhPHqS8EBFJFpls/pB1gKsEmTwbJu9fBxN4fZfUFPuTnCIJsrEsnyRfeNTAUFYl3hhlRYZo5GiQ==</InverseQ><D>qB8WvAmWFMW67EM8mdlReI7L7jK4bVf+YXOtJzVwfJ2PXtoUI+wTgH0Su0IRp9sR/0v/x9HZlluj0BR2O33snQCxYI8LIo5NoWhfhkVSv0QFQiDcG5Wnbizz7w2U6pcxEC2xfcoKG4yxFkAmHCIkgs/B9T86PUPSW4ZTXcwDmqU=</D></RSAKeyValue>";

   rsa.FromXmlString(rsaPrivateKey);
   String signedData = "<SignedData><Key>Insert your key here</Key></SignedData>";
   Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(signedData);
   Byte[] sigBytes = rsa.SignData(licenseData, new SHA1CryptoServiceProvider());
   String sigText = System.Text.Encoding.UTF8.GetString(Transform(sigBytes, new ToBase64Transform()));
   System.Text.StringBuilder sb = new StringBuilder();
   using (System.Xml.XmlWriter xw = System.Xml.XmlTextWriter.Create(sb))
   {
      xw.WriteStartElement("License");
      xw.WriteRaw(signedData);
      xw.WriteElementString("Signature", sigText);
      xw.WriteEndElement();
   }
   return sb.ToString();
}

Example output from this code:

<?xml version="1.0" encoding="utf-16"?>
<License>
  <SignedData>
    <Key>Insert your key here</Key>
  </SignedData>
  <Signature>cgpmyqaDlHFetCZbm/zo14NEcBFZWaQpyHXViuDa3d99AQ5Dw5Ya8C9WCHbTiGfRvaP4nVGyI+ezAAKj287dhHi7l5fQAggUmh9xTfDZ0slRtvYD/wISCcHfYkEhofXUFQKFNItkM9PnOTExZvo75pYPORkvKBF2UpOIIFvEIU=</Signature>
</License>

Then you can use code like this to verify it. You never have to distribute the private key:

public static Boolean CheckLicenseSignature(String licXml)
{
   try
   {
      System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
      xd.LoadXml(licXml);
      String licSig = xd.SelectSingleNode("/License/Signature").InnerText;
      RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
      String rsaPublicKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
      rsa.FromXmlString(rsaPublicKey);
      Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(xd.SelectSingleNode("/License/SignedData").OuterXml);
      return rsa.VerifyData(licenseData, new SHA1CryptoServiceProvider(), Transform(System.Text.Encoding.UTF8.GetBytes(licSig), new FromBase64Transform()));
   }
   catch (System.Xml.XmlException ex)
   {
      return false;
   }
   catch (InvalidOperationException ex)
   {
      return false;
   }
}
BlueMonkMN
Again the file must be self contained. I cannot have any dependent files, but this is a pretty awesome solution if I could have another file to ride along. Also the key must be able to be read as encryption isn't needed. There is another process in the security that will lock down security. I just need to have a unique identifier associated with the exe and it needs to be embedded with each exe I send out.
nitefrog
I thought you might be solving the wrong problem because I think in many cases the app's config file is automatically transported with the executable. And there are also ways of packaging multiple files into one if necessary. But if those problems are harder to deal with than the solution you've accepted, certainly take the path of least resistance.
BlueMonkMN
Also, the key does not get encrypted by this code. The entire XML is plain text, and only the signature added to the end is incomprehensible.
BlueMonkMN