0

I have a class library which by default doesn't have an app.config. The calling app for this library is "explorer.exe" and I won't be able to use explorer.exe.config to add my settings.

Is there any way I can have my class library read an app.config? It needs to be an app.config because I intend on encrypting it during deployment using aspnet_regiis (I'll rename it web.config, encrypt it and rename it back to app.config).

Sniipe
  • 1,116
  • 3
  • 13
  • 28
  • 1
    Are you aware that anything your app can locally decrypt can also be decrypted by a user of the machine? You are only securing your secrets from the unsaavy user so if the information is truly sensitive you will need a different approach. – Crowcoder Jun 13 '18 at 10:52
  • hi @Crowcoder, I'm following the instructions here https://msdn.microsoft.com/en-us/library/zhhddkxy.aspx is it not secure? – Sniipe Jun 13 '18 at 11:28
  • 1
    That's correct. It is not secure. Anyone with a little knowledge of .net can use the DPAPI to decrypt it. The reason it is secure for a web server is that users are not logged into the server itself and generally web server access is restricted to trusted personnel. – Crowcoder Jun 13 '18 at 12:21
  • 1
    You can control the _entropy_ used in the encryption/decryption so that it becomes a bit more difficult (the attacker would need to find the entropy used in your code -- see [https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata.protect?view=netframework-4.7.2](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata.protect?view=netframework-4.7.2) – Stand__Sure Jun 13 '18 at 12:43

2 Answers2

2

In C# the only config that matters really is the app.config of the output project. In the case of a console app this will be the .exe config. Which will appear in the bin as {your app name}.exe.config.

You can read this file using the ConfigurationManager in the System.Configuration DLL. All the uses of this will point to the executing code's configuration file, even in a class library. So any additional configuration needed in an imported class library will need to be added to this file. This is the canonical way of dealing with config.

If you really want to have some other configuration file, you can use:

ConfigurationManager.OpenMappedExeConfiguration(
            new ExeConfigurationFileMap
            {
                ExeConfigFilename = overrideConfigFileName
            }, 
            ConfigurationUserLevel.None)

Where overrideConfigFileName points to your other app.config file. You can set the file in the class library as Content and ensure it is copied into the output directory at build time. Then you will have to ensure that it is included in the final deploy package and all the paths match.

tigerswithguitars
  • 2,497
  • 1
  • 31
  • 53
  • Thanks for the response. However, this is not a console app. This is a tweak to windows explorer. I don't have an exe. – Sniipe Jun 13 '18 at 10:40
  • 1
    Then you'll want to go another route than a config file -- you can still encrypt during install and decrypt on the fly using the DPAPI see [https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection](https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection) – Stand__Sure Jun 13 '18 at 12:01
  • Hi @Stand__Sure, I like this answer. I'll try it out. How would I get the encrypted secret onto the machine initially? IE how do I "encrypt during install" ? – Sniipe Jun 13 '18 at 14:19
  • 1
    @Sniipe Add a step in the installer process -- this will vary by the installer you're using If you're not doing an installer, an alternative approach is to check if the thing is encrypted on application start and if not then encrypt (this has the benefit that you can replace the file after installation and have it re-encrypt) – Stand__Sure Jun 13 '18 at 17:17
  • 1
    @Stand__Sure this is exactly how I do it currently. There is a moment during the install and run that the setting is not encrypted. Obviously you need to ensure that the installer package itself can't be easily read to get the secrets before the installing process encrypts things. – tigerswithguitars Jun 14 '18 at 10:04
  • Would this be ok: Include a settings.dat with an unencrypted secret at a static location on the filesystem. Then during deployment I will run a console application which will encrypt it. Then when a user uses my app it will unencrypt the settings.dat during startup. – Sniipe Jun 14 '18 at 10:09
0

In the end (as per @Stand__Sure and @tigerswithguitars I created a new project within my solution which will be a console App. It will be executed at deployment. Thanks to Stand__Sure for his link to https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection

The console app does the following:

private static void Run()
{
    try
    {
        // Get unencrypted data from Settings.dat
        string[] unencrypted = File.ReadAllLines("C:\\Program Files (x86)\\theAPPSettings\\Settings.dat");

        string unencryptedGuid = unencrypted[0]; //its only 1 setting that I'm interested in

        // Create a file.
        FileStream fStream = new FileStream("C:\\Program Files (x86)\\theAPPSettings\\ProtectedSettings.dat", FileMode.OpenOrCreate);

        byte[] toEncrypt = UnicodeEncoding.ASCII.GetBytes(unencryptedGuid);                

        byte[] entropy = UnicodeEncoding.ASCII.GetBytes("A Shared Phrase between the encryption and decryption");

        // Encrypt a copy of the data to the stream.
        int bytesWritten = Protection.EncryptDataToStream(toEncrypt, entropy, DataProtectionScope.CurrentUser, fStream);

        fStream.Close();

        File.Delete("C:\\Program Files (x86)\\theAPPSettings\\Settings.dat");

        //Console.ReadKey();
    }
    catch (Exception e)
    {
        Console.WriteLine("ERROR: " + e.Message);
    }
}

The calling app decrypts it as follows:

FileStream fStream = new FileStream("C:\\Program Files (x86)\\theAPPSettings\\ProtectedSettings.dat", FileMode.Open);

byte[] entropy = UnicodeEncoding.ASCII.GetBytes("A Shared Phrase between the encryption and decryption");

// Read from the stream and decrypt the data.
byte[] decryptData = Protection.DecryptDataFromStream(entropy, DataProtectionScope.CurrentUser, fStream, Length_of_Stream);

fStream.Close();

string temp = UnicodeEncoding.ASCII.GetString(decryptData);
Sniipe
  • 1,116
  • 3
  • 13
  • 28