lundi 27 avril 2015

Secure connection strings in app.config for a desktop application

The goal is to get away as little as possible of the standard.

In this case the standard is : Encrypting Configuration Information Using Protected Configuration

In short: use a ProtectedConfigurationProvider. The .Net Framework provides two of them:

The main inconvenience of those providers comes from the fact that they have been designed for web applications: that are applications in which the securisable is the application server. That is one web.config in one machine

With a desktop application you have a plurality of app.config

Said providers are machine based, i.e. there is one configuration file per machine. In other words, the configuration file of one machine can't be used by another because of different encryption keys.

Fortunately the .Net Framework provides an abstract class that can be overridden to built a custom provider.

Here is a sample

The DLL:

using System;
using System.Configuration;
using System.Xml;

namespace MBT.ProtectedConfigurationProviders {

    public class WeakProtectedConfigurationProvider : ProtectedConfigurationProvider {        
        public override void Initialize(string name, 
            System.Collections.Specialized.NameValueCollection config) {
            
            base.Initialize(name, config);
        }

        public override System.Xml.XmlNode Decrypt(System.Xml.XmlNode encryptedNode) {
            string decryptedData =
                DecryptString(encryptedNode.InnerText);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.LoadXml(decryptedData);

            return xmlDoc.DocumentElement;
        }

        public override System.Xml.XmlNode Encrypt(System.Xml.XmlNode node) {
            string encryptedData = EncryptString(node.OuterXml);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.LoadXml("" +
                encryptedData + "");

            return xmlDoc.DocumentElement;
        }

        public static String EncryptString(String st) {
            return Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(st));
        }

        public static String DecryptString(String st) {
            return WeakProtectedConfigurationProvider.GetString(Convert.FromBase64String(st));
        }

        public static byte[] GetBytes(string str) {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        public static string GetString(byte[] bytes) {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }
    }

}

Here the WEAK word is deliberately chosen. Base 64 encoding is not at all a cypher algorithm.

Then the Program:

using System;
using System.Configuration;

namespace bas {
    class Program {
        static void Main(string[] args) {
            //String cs = @"";
            //Console.WriteLine(Convert.ToBase64String(WeakProtectedConfigurationProvider.GetBytes(cs)));
            Console.WriteLine(ConfigurationManager.ConnectionStrings["SomeName"].ConnectionString);
        }
    }
}

And finally the app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration >
  <!-- xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"-->
   
  <configProtectedData defaultProvider="WeakProtectedConfigurationProvider">
    <providers>
      <clear />
      <add name ="WeakProtectedConfigurationProvider" type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider, WeakProtectedConfigurationProvider"/>
    </providers>
  </configProtectedData>

  <connectionStrings configProtectionProvider="WeakProtectedConfigurationProvider">
    <EncryptedData>
      <CipherData>
        <CipherValue>PABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAHMAPgA8AGEAZABkACAAbgBhAG0AZQA9ACIAUwBvAG0AZQBOAGEAbQBlACIAIABjAG8AbgBuAGUAYwB0AGkAbwBuAFMAdAByAGkAbgBnAD0AIgBEAGEAdABhACAAUwBvAHUAcgBjAGUAPQBTAG8AbQBlAFMAZQByAHYAZQByADsASQBuAGkAdABpAGEAbAAgAEMAYQB0AGEAbABvAGcAPQBTAG8AbQBlAEQAYQB0AGEAQgBhAHMAZQA7AEkAbgB0AGUAZwByAGEAdABlAGQAIABTAGUAYwB1AHIAaQB0AHkAPQBUAHIAdQBlADsAQwBvAG4AbgBlAGMAdAAgAFQAaQBtAGUAbwB1AHQAPQAxADgAMAA7AE0AdQBsAHQAaQBwAGwAZQBBAGMAdABpAHYAZQBSAGUAcwB1AGwAdABTAGUAdABzAD0AVAByAHUAZQAiACAAcAByAG8AdgBpAGQAZQByAE4AYQBtAGUAPQAiAFMAeQBzAHQAZQBtAC4ARABhAHQAYQAuAFMAcQBsAEMAbABpAGUAbgB0ACIAIAAvAD4APAAvAGMAbwBuAG4AZQBjAHQAaQBvAG4AUwB0AHIAaQBuAGcAcwA+AA==</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>
  
</configuration>

At least two points are significant here. First:

<add name ="WeakProtectedConfigurationProvider"
    type="MBT.ProtectedConfigurationProviders.WeakProtectedConfigurationProvider,  
          WeakProtectedConfigurationProvider"/>

Here to avoid a GAC registration, on use the syntax "type, assembly"

Second:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

One may want to use a namespace in configuration tag of app.config to avoid Visual Studio complaint. But, in my case this also leads to side effect: "side by side configuration error" which prevents application from starting. So, for now, I do without, and it runs well.

Thank you to StackOverflow for the StringToBytesToString.

samedi 25 avril 2015

Reset visual studio to have it to forget you !

From time to time you may want to connect as another one to visual studio. That is not so easy as it seems. Even the official documentation is not of great help.

Said documentation points out a ResetSettings flag, but it does not do the trick.

Hopefully, and as always, StackOverflow is helpfull, and the solution is:

devenv /resetuserdata

mardi 21 avril 2015

SSRS: pass mutivalued parameter by http

In an SSRS report you may have multivaluable parameters :


In this case the question is: how to pass such a paramter by http ?
The answer is quite simple. In your query you must have a sequence like:
&CRITS=CRIT1&CRITS=CRIT2

The number of &CRITS= depends on the number of values.