Debug Windows Service in Visual Studio

In a lot of projects I use Windows Services to host services in Client-Server scenarios. In order to debug the service Microsoft recommends to install the service, start it and attach the Visual Studio debugger to the process.

Doing it this way you have to be aware of these things:

  • The service must be installed on your machine using installutil
  • You must stop the service each time you build your project because otherwise the .exe is locked
  • Don´t forget to uninstall the service before you change the namespace of the project or if you move/delete it. Otherwise the service instance remains installed and you have to remove the appropriate registry keys manually
  • Too many clicks for such a simple thing 🙂

You can also use frameworks like Topshelf to ease your development. But often the customer isn´t interested in getting a product that contains frameworks others than Microsoft´s and sometimes, as a developer, you don´t see the need of a separate framework.

Hence I used a simple but effective way at Tekaris to solve that problem, demonstrated by the following example project:

  1. Set the type of the project that contains the Windows service to “Console Application”
    consoleapp
  2. Add a public method Start to your service and move the logic of your OnStart method to it
  3. Call the Start method in the OnStart method
    public class MyWindowsService : ServiceBase
    {
        public void Start()
        {
            // Do your work here
         }
    
         protected override void OnStart(string[] args)
         {
             Start();
         }
    
         protected override void OnStop()
         {
             // Do you cleanup here
         }
     }
    
  4. Create and start the service in the Main method of the Program.cs of the Windows service project
    private static void Main(string[] args)
    {
        var service = new MyWindowsService();
    
        if (Environment.UserInteractive)
        {
            Console.WriteLine("Starting {0}...", service.GetType().Name);
            service.Start();
            Console.WriteLine("{0} is running...", service.GetType().Name);
            Console.WriteLine("Enter C to stop the service.");
    
            var input = Console.ReadLine();
            while (input != "C")
                input = Console.ReadLine();
        }
        else
            ServiceBase.Run(new ServiceBase[] {service});
    }
    

The “trick” is to check whether the application runs in an user interactive environment. That´s the case if you start your solution in Visual Studio. The public Start method is necessary because the protected OnStart method of the service is called by the framework.

if (Environment.UserInteractive)
{
    ...
}

When you install the service via the installutil command and start it via the “Local Services” snapin, the environment is not user interactive and the default behavior for starting a Windows service takes place.

ServiceBase.Run(new ServiceBase[] {service});
Advertisements

Encrypting large data with asymmetric RSACryptoServiceProvider

The .NET framework provides an easy way to encrypt and decrypt sensitive data using the RSACryptoServiceProvider. There are only a few steps necessary to get the encryption working via several machines:

  • Create the key container on one machine and allow to export the private key:
    aspnet_regiis -pc <KeyContainerName> -exp
  • Export the key container:
    aspnet_regiis -px <KeyContainerName> <PathToExportXML>
  • Import the key container on the other machines:
    aspnet_regiis -pi <KeyContainerName> <PathToExportXML>
  • Grant access for appropriate accounts:
    aspnet_regiis -pa <KeyContainerName> <AccountName>

Encrypting and decrypting are straight forward API-calls:

public class RSAEncryptionService
{
    private const string ProviderName = "Microsoft Strong Cryptographic Provider";
    private const int ProviderType = 1;

    protected RSACryptoServiceProvider CreateProvider()
    {
        return new RSACryptoServiceProvider(new CspParameters(ProviderType, ProviderName)
        {
            KeyContainerName = "KeyContainerName",
            Flags = CspProviderFlags.UseExistingKey |
            CspProviderFlags.UseMachineKeyStore
        });
    }

    public string Encrypt(string decryptedData)
    {
        var encryptedBytes = CreateProvider().Encrypt(Encoding.Default.GetBytes(decryptedData), true);
        return Encoding.Default.GetString(encryptedBytes);
    }

    public string Decrypt(string encryptedData)
    {
       var decryptedBytes = CreateProvider().Decrypt(Encoding.Default.GetBytes(encryptedData), true);
       return Encoding.Default.GetString(decryptedBytes);
    }
}

So as you see with these steps you can set up an application server environment that ensures, that your applications encryption logic is working on every machine.

But what´s the problem when you run into a “Bad Length” CryptographicException? I got such an exception lately at Tekaris in a customer project. You won´t face this problem if you only encrypt “small” data like passwords with a widely seen length of 8-16 characters. You face it when you try to encrypt a string with more than 87 characters. The reason is that RSA can only encrypt data blocks that are shorter than the key length. One the one hand you could switch over to symmetric encryption but sometimes it´s not what you intentionally wanted. On the other hand you can stay with asymmetric encryption and adjust the above code example:

public class RSAEncryptionService
{
    private const string ProviderName = "Microsoft Strong Cryptographic Provider";
    private const int ProviderType = 1;
    private const int SegmentLength = 85;
    private const int EncryptedLength = 128;

    protected RSACryptoServiceProvider CreateProvider()
    {
        return new RSACryptoServiceProvider(new CspParameters(ProviderType, ProviderName)
                                                {
                                                    KeyContainerName = "KeyContainerName",
                                                    Flags = CspProviderFlags.UseExistingKey |
                                                            CspProviderFlags.UseMachineKeyStore
                                                });
    }

    public string Encrypt(string decryptedData)
    {
        var length = decryptedData.Length/SegmentLength + 1;
        var sb = new StringBuilder();

        for (var i = 0; i < length; i++)
        {
            int lengthToCopy;
            if (i == length - 1 || decryptedData.Length < SegmentLength)
                lengthToCopy = decryptedData.Length - (i*SegmentLength);
            else
                lengthToCopy = SegmentLength;

            var segment = decryptedData.Substring(i*SegmentLength, lengthToCopy);
            sb.Append(Encrypt(CreateProvider(), segment));
        }

        return sb.ToString();
    }

    public string Decrypt(string encryptedData)
    {
        var length = encryptedData.Length/EncryptedLength;
        var sb = new StringBuilder();

        for (var i = 0; i < length; i++)
        {
            var segment = encryptedData.Substring(i*EncryptedLength, EncryptedLength);
            sb.Append(Decrypt(CreateProvider(), segment));
        }

        return sb.ToString();
    }

    protected string Encrypt(RSACryptoServiceProvider rsa, string decryptedData)
    {
        var encryptedBytes = rsa.Encrypt(Encoding.Default.GetBytes(decryptedData), true);
        return Encoding.Default.GetString(encryptedBytes);
    }

    protected string Decrypt(RSACryptoServiceProvider rsa, string encryptedData)
    {
        var decryptedBytes = rsa.Decrypt(Encoding.Default.GetBytes(encryptedData), true);
        return Encoding.Default.GetString(decryptedBytes);
    }
}

The solution is to split the data, encrypt the segments and join them again. Of course the performance is not as good as with symmetric encryption. But there are two things to mention:

  • Check how often you are really using this logic in your application lifecycle and hence is it really performance critical?
  • You can remain with your (eventually environment depending) decision to use asymmetric encryption

An example is to execute an operation with an impersonated account and the credentials of that account are stored in your configuration database. Do you really retrieve the password from the database every time you need to impersonate or does your configuration service internally cache the decrypted setting? Well if not, the account must be a companywide-godfather-account to need such a complex password 🙂