Encrypting with Certificates

.NET provides us with several algorithm implementations to perform encryption and decryption. Next you can see an easy way to make use of a certificate to encrypt / decrypt  using the RSACryptoServiceProvider

The first thing we need to do is to create a test certificate, this can be done by using the tool makecert. Just open the VS 2005 command prompt and type the next line:

makecert -n CN=Test -pe -ss My -sr LocalMachine

The command above will create a certificate called "Test" and will install it in the "My" location (displayed as "Personal") of the Local Computer, the -pe will mark the private key as exportable.

You can see the test certificate you have created following the next steps:

1. Run the Microsoft Management Console (MMC) snap-in utility by running the command mmc from a command prompt.
2. On the File menu, click Add/Remove Snap-in.
3. In the dialog box that displays, click Add.
4. Under Snap-in, double-click Certificates.
5. Click Computer account to access the LocalMachine store, and then click Next.
6. Click Finish.
7. Click the Certificates node, Personal, Certificates and et voila, your certificate is there.

Now let's see how we can make use of the certificate.

   1: public static X509Certificate2 LoadCertificate(StoreName storeName, 
   2:         StoreLocation storeLocation, string certificateName)
   3: {
   4:     X509Store store = new X509Store(storeName, storeLocation);
   5:  
   6:     try
   7:     {
   8:         store.Open(OpenFlags.ReadOnly);
   9:  
  10:         X509Certificate2Collection certificateCollection =
  11:             store.Certificates.Find(X509FindType.FindBySubjectName, 
  12:                                     certificateName, false);
  13:  
  14:         if (certificateCollection.Count > 0)
  15:         {
  16:             //  We ignore if there is more than one matching cert, 
  17:             //  we just return the first one.
  18:             return certificateCollection[0];
  19:         }
  20:         else
  21:         {
  22:             throw new ArgumentException("Certificate not found");
  23:         }
  24:     }
  25:     finally
  26:     {
  27:         store.Close();
  28:     }
  29: }
  30:  
  31: public static byte[] Encrypt(byte[] plainData, bool fOAEP, 
  32:                              X509Certificate2 certificate)
  33: {
  34:     if (plainData == null)
  35:     {
  36:         throw new ArgumentNullException("plainData");
  37:     }
  38:     if (certificate == null)
  39:     {
  40:         throw new ArgumentNullException("certificate");
  41:     }
  42:  
  43:     using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
  44:     {
  45:         // Note that we use the public key to encrypt
  46:         provider.FromXmlString(GetPublicKey(certificate));
  47:  
  48:         return provider.Encrypt(plainData, fOAEP);
  49:     }
  50: }
  51:  
  52: public static byte[] Decrypt(byte[] encryptedData, bool fOAEP, 
  53:                              X509Certificate2 certificate)
  54: {
  55:     if (encryptedData == null)
  56:     {
  57:         throw new ArgumentNullException("encryptedData");
  58:     }
  59:     if (certificate == null)
  60:     {
  61:         throw new ArgumentNullException("certificate");
  62:     }
  63:  
  64:     using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
  65:     {
  66:         // Note that we use the private key to decrypt
  67:         provider.FromXmlString(GetXmlKeyPair(certificate));
  68:  
  69:         return provider.Decrypt(encryptedData, fOAEP);
  70:     }
  71: }
  72:  
  73: public static string GetPublicKey(X509Certificate2 certificate)
  74: {
  75:     if (certificate == null)
  76:     {
  77:         throw new ArgumentNullException("certificate");
  78:     }
  79:  
  80:     return certificate.PublicKey.Key.ToXmlString(false);
  81: }
  82:  
  83: public static string GetXmlKeyPair(X509Certificate2 certificate)
  84: {
  85:     if (certificate == null)
  86:     {
  87:         throw new ArgumentNullException("certificate");
  88:     }
  89:  
  90:     if (!certificate.HasPrivateKey)
  91:     {
  92:         throw new ArgumentException("certificate does not have a private key");
  93:     }
  94:     else
  95:     {
  96:         return certificate.PrivateKey.ToXmlString(true);
  97:     }
  98: }

The code to use the above methods is very simple:

   1: X509Certificate2 cert = LoadCertificate(
   2:      System.Security.Cryptography.X509Certificates.StoreName.My,
   3:      System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, "Test");
   4:  
   5: byte[] encoded = System.Text.UTF8Encoding.UTF8.GetBytes("Encrypt me");
   6:  
   7: byte[] encrypted = Encrypt(encoded, true, cert);
   8: byte[] decrypted = Decrypt(encrypted, true, cert);
   9:  
  10: System.Console.Out.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(decrypted));

.NUG Baleares on .NET

I'm proud to announce that a new .NET Users Group has been created in the Balearic Islands (Spain).

Baleares on .NET

The group will try to introduce .net technologies to the developers community through different online and presential activities.

You can join us at http://www.baleareson.net (Spanish)

Handling Network Connections

Talking again about my virus there was another nice thing it was doing, the virus opened IE windows redirecting to an antivirus page (it was an ironic virus).

The cool thing is that it only tried to do it when I was connected to my router, setting the browser to work offline was not enough since it tried to connect anyway in the background, but when I had my cable disconnected the virus didn't try. So I guess the virus was monitoring the network status.

To reproduce this I think the best would be to use WMI, because it provides us all kind of information regarding the system including this one. I've build a very simple class that catches the WMI events and throws a custom event notifying that the cable is connected or disconnected.

If you want to build an application that makes use of it you only need to create the handlers for the events NetworkConnect and NetworkDisconnect, and use the methods Start and Stop for monitoring the network, I think they are quite descriptive. Don't forget to call Dispose when are finished with the NetworkManager.

   1: public class NetworkEventArgs
   2: {
   3:     string instanceName;
   4:  
   5:     public string InstanceName
   6:     {
   7:         get { return instanceName; }
   8:     }
   9:  
  10:     public NetworkEventArgs(string name)
  11:     {
  12:         instanceName = name;
  13:     }
  14: }
  15:  
  16: public class NetworkManager : IDisposable
  17: {
  18:     private ManagementEventWatcher wmiDisconnect;
  19:     private ManagementEventWatcher wmiConnect;
  20:  
  21:     public delegate void NetworkEventHandler(object sender, NetworkEventArgs e);
  22:  
  23:     public event NetworkEventHandler NetworkDisconnect;
  24:     public event NetworkEventHandler NetworkConnect;
  25:  
  26:     private bool disposed;
  27:  
  28:     #region Constructor
  29:     public NetworkManager()
  30:     {
  31:         wmiDisconnect = new ManagementEventWatcher(
  32:                 "SELECT * FROM MSNdis_StatusMediaDisconnect");
  33:         wmiConnect = new ManagementEventWatcher(
  34:                 "SELECT * FROM MSNdis_StatusMediaConnect");
  35:  
  36:         wmiDisconnect.Scope = new ManagementScope(@"root\WMI");
  37:         wmiConnect.Scope = new ManagementScope(@"root\WMI");
  38:  
  39:         wmiDisconnect.EventArrived += new EventArrivedEventHandler(WMIDisconnect_EventArrived);
  40:         wmiConnect.EventArrived += new EventArrivedEventHandler(WMIConnect_EventArrived);
  41:     }
  42:     #endregion Stop
  43:  
  44:     #region Wmi Events
  45:     private void WMIDisconnect_EventArrived(object sender, EventArrivedEventArgs e)
  46:     {
  47:         string instanceName = e.NewEvent.Properties["InstanceName"].Value as string;
  48:  
  49:         OnNetworkDisconnect(instanceName);
  50:     }
  51:  
  52:     private void WMIConnect_EventArrived(object sender, EventArrivedEventArgs e)
  53:     {
  54:         string instanceName = e.NewEvent.Properties["InstanceName"].Value as string;
  55:  
  56:         OnNetworkConnect(instanceName);
  57:     }
  58:     #endregion Wmi Events
  59:  
  60:     #region Event Firing
  61:     protected void OnNetworkConnect(string instanceName)
  62:     {
  63:         NetworkEventHandler networkConnect = NetworkConnect;
  64:  
  65:         if (networkConnect != null)
  66:             NetworkConnect(this, new NetworkEventArgs(instanceName));
  67:     }
  68:  
  69:     protected void OnNetworkDisconnect(string instanceName)
  70:     {
  71:         NetworkEventHandler networkDisconnect = NetworkDisconnect;
  72:  
  73:         if (networkDisconnect != null)
  74:             NetworkDisconnect(this, new NetworkEventArgs(instanceName));
  75:     }
  76:     #endregion Event Firing
  77:  
  78:     #region Start
  79:     public void Start()
  80:     {
  81:         if (disposed)
  82:             throw new ObjectDisposedException("NetworkManager");
  83:  
  84:         wmiDisconnect.Start();
  85:         wmiConnect.Start();
  86:     }
  87:     #endregion Start
  88:     
  89:     #region Stop
  90:     public void Stop()
  91:     {
  92:         if (disposed)
  93:             throw new ObjectDisposedException("NetworkManager");
  94:  
  95:         wmiDisconnect.Stop();
  96:         wmiConnect.Stop();
  97:     }
  98:     #endregion Stop
  99:  
 100:     #region Dispose Related
 101:     public void Dispose()
 102:     {
 103:         Dispose(true);
 104:  
 105:         GC.SuppressFinalize(this);
 106:     }
 107:  
 108:     protected virtual void Dispose(bool disposing)
 109:     {
 110:         if (!this.disposed)
 111:         {
 112:             if (disposing)
 113:             {
 114:                 Stop();
 115:  
 116:                 wmiConnect.Dispose();
 117:                 wmiDisconnect.Dispose();
 118:             }
 119:             disposed = true;
 120:         }
 121:     }
 122:  
 123:     ~NetworkManager()
 124:     {
 125:         Dispose(false);
 126:     }
 127:     #endregion Dispose Related
 128: }

Accessing UI controls with Multithreading applications

If you work with multithreading applications, probably you will need to access UI controls created in the main thread from a different thread. As you know, one of the rules of Windows programming, is that the changes done over UI controls must be done from the thread that created the control. Otherwise with .net 2.0 an exception will be thrown at execution time, but in previous versions of .net it is worse since you can obtain unexpected results.

Let's see a simple sample that would create troubles:

   1: private void bttStart_Click(object sender, System.EventArgs e)
   2: {
   3:     System.Threading.Thread thread = new System.Threading.Thread(
   4:         new System.Threading.ThreadStart(DoWork));
   5:     thread.Name = "Worker Thread";
   6:     thread.IsBackground = true;
   7:     thread.Start();
   8: }
   9:  
  10: private void DoWork()
  11: {
  12:     int iteration = 0;
  13:     while (true)
  14:     {
  15:         iteration++;
  16:         textBox1.Text = iteration.ToString(
  17:             System.Globalization.CultureInfo.InvariantCulture);
  18:         System.Threading.Thread.Sleep(3000);
  19:     }
  20: }

Therefore this is avoidable just adding a few lines more. All the UI controls have a property called InvokeRequired, which indicates if the caller comes from a different thread than the one that created the control. In this case we only need to invoke the method again in the right thread. Let's see how to fix it:

   1: public delegate void TextUpdate(string text);
   2:  
   3: private void SetText(string text)
   4: {
   5:     if (textBox1.InvokeRequired)
   6:     {
   7:         textBox1.Invoke(new TextUpdate(SetText), text);
   8:     }
   9:     else
  10:     {
  11:         textBox1.Text = text;
  12:     }
  13: }

As you can see the changes are very simple. We have created a method called SetText, which checks if the method that is going to modify the Text property is in a different thread, if that's the case it creates an instance of the TextUpdate delegate. The delegate just points to the method SetText. Now when you call the Invoke method of the TextBox, the only thing that it will do is to call again the method SetText but this time from the thread which created the control.

Registry Handling

These days I’ve been bothered by a virus that decided to live on my laptop.

I’m not going to explain how it arrived there, because I get really mad, I’m just going to give a common sense suggestion: never, never let your laptop to other people, doesn’t matter how friends you are ….

In any case there was an interesting part on all this, since the virus had nice things to learn which I’m going to try to reproduce using .NET:

The virus introduced lot of keys in the registry; one of the most interesting was added to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify. Maybe in another post I will explain why this one is interesting, but first let’s try to add or modify registry keys from C#.

The .NET Framework, once more, has done most of the work for us. We only need to take a look to the Microsoft.Win32 namespace and there we will find the classes "Registry" and "RegistryKey", which makes extremely easy to work with the Windows Registry.

Take a look to the next code:

   1: string keyName = @"Software\Microsoft\Windows NT\CurrentVersion\WinLogon\Notify";
   2:  
   3: RegistryKey notifyKey = Registry.LocalMachine.OpenSubKey(keyName, true); 
   4: RegistryKey testKey = notifyKey.CreateSubKey("TestKey"); 
   5:  
   6: testKey.SetValue("Asynchronous", 0x00000001, RegistryValueKind.DWord); 
   7: testKey.SetValue("DllName", "FakeVirus.dll", RegistryValueKind.String); 
   8: testKey.SetValue("Impersonate", 0x00000000, RegistryValueKind.DWord); 
   9: testKey.SetValue("Logoff", "Logoff", RegistryValueKind.String); 
  10: testKey.SetValue("Logon", "Logon", RegistryValueKind.String);

As you already deducted we have created a new sub key called TestKey and introduced in it 5 different values, Asynchronous, DllName, Impersonate … very easy, isn't it?

With the virus I wasn’t able to delete the registry entries added because they were regenerated immediately after any kind of change. This was done monitoring the entries, something that cannot be done directly with the managed classes but Windows supports through its API.

Reproduce this mechanism is not a big problem since even if .NET doesn’t have an equivalent method we can use p/invoke (Platform Invoke) to use the unmanaged functions of the Win32 API. For more information about it you can check the next link: http://msdn2.microsoft.com/en-us/library/ms724892.aspx

I add here the most interesting part of the code in order you can build your own monitor:

   1: // P/Invoke methods and constants 
   2:  
   3: internal class NativeMethods
   4: {
   5:    private NativeMethods()
   6:    {
   7:    } 
   8:  
   9:    [DllImport("advapi32.dll", SetLastError = true)]
  10:    internal static extern int RegNotifyChangeKeyValue(
  11:       IntPtr hKey,
  12:       [MarshalAs(UnmanagedType.Bool)] 
  13:       bool watchSubtree,
  14:       int dwNotifyFilter, 
  15:       IntPtr hEvent, 
  16:       [MarshalAs(UnmanagedType.Bool)] 
  17:       bool fAsynchronous 
  18:    ); 
  19:  
  20:    [DllImport("advapi32.dll", SetLastError = true)]
  21:    internal static extern int RegOpenKeyEx(
  22:       IntPtr hKey,
  23:       string subKey,
  24:       uint options,
  25:       int sam,
  26:       out IntPtr phkResult 
  27:    ); 
  28:  
  29:    [DllImport("advapi32.dll", SetLastError = true)]
  30:    internal static extern int RegCloseKey(
  31:       IntPtr hKey 
  32:    ); 
  33:  
  34:    internal const int HKEY_CLASSES_ROOT = unchecked((int)0x80000000);
  35:    internal const int HKEY_CURRENT_USER = unchecked((int)0x80000001);
  36:    internal const int HKEY_LOCAL_MACHINE = unchecked((int)0x80000002);
  37:    internal const int HKEY_USERS = unchecked((int)0x80000003);
  38:    internal const int HKEY_PERFORMANCE_DATA= unchecked((int)0x80000004);
  39:    internal const int HKEY_CURRENT_CONFIG = unchecked((int)0x80000005);
  40:    internal const int HKEY_DYN_DATA = unchecked((int)0x80000006); 
  41:  
  42:    internal const int KEY_NOTIFY = 0x0010; 
  43:  
  44:    internal const int REG_NOTIFY_CHANGE_NAME = 0x00000001;
  45:    internal const int REG_NOTIFY_CHANGE_ATTRIBUTES = 0x00000002;
  46:    internal const int REG_NOTIFY_CHANGE_LAST_SET = 0x00000004;
  47:    internal const int REG_NOTIFY_CHANGE_SECURITY = 0x00000008;
  48: } 
  49:  
  50: .................................
  51:  
  52: IntPtr parentKey = new IntPtr(NativeMethods.HKEY_LOCAL_MACHINE); 
  53:  
  54: string subKey = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\TestKey" 
  55:  
  56: IntPtr monitoredKey; 
  57:  
  58: try 
  59: { 
  60:    int ret = NativeMethods.RegOpenKeyEx(parentKey, subKey, 0, NativeMethods.KEY_NOTIFY, out monitoredKey);
  61:    if (ret != 0)
  62:    {
  63:       throw new Win32Exception(ret);
  64:    } 
  65:  
  66:    using (AutoResetEvent monitorEvent = new AutoResetEvent(false))
  67:    {
  68:       ret = NativeMethods.RegNotifyChangeKeyValue(parentKey, true, 
  69:                         NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_ATTRIBUTES | 
  70:                         NativeMethods.REG_NOTIFY_CHANGE_LAST_SET | NativeMethods.REG_NOTIFY_CHANGE_SECURITY, 
  71:                         monitorEvent.Handle, true); 
  72:  
  73:       if (ret != 0)
  74:       {
  75:          throw new Win32Exception(ret);
  76:       }  
  77:  
  78:       monitorEvent.WaitOne();
  79:       // Do something after the registry has changed 
  80:    } 
  81: } 
  82: finally
  83: {
  84:    parentKey = IntPtr.Zero;
  85:    if (monitoredKey != IntPtr.Zero)
  86:    {
  87:       NativeMethods.RegCloseKey(monitoredKey);
  88:       monitoredKey = IntPtr.Zero;
  89:    } 
  90: }

Nice, don't you think?