Microsoft & .NET.NETImproved .NET Remoting, Part 2: Secure TCP

Improved .NET Remoting, Part 2: Secure TCP

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

.NET remoting enables application communication. It is a generic system that different applications can use to communicate with one another. .NET objects are exposed to remote processes, thus allowing interprocess communication. The applications can be located on the same computer, different computers on the same network, or even computers across separate networks.

Remoting communication is not secure by default, however. The 1.0 and 1.1 versions of the Microsoft Framework offered two options for making it secure:

  1. Use the HttpChannel, host the remoting server inside of IIS, and use SSL to secure communication between the client and server.
  2. Do something similar to this example from Microsoft, which is complicated, not supported, and requires you to create custom code.

The 2.0 version of the Microsoft Framework provides additional security-related functionality within the System.Runtime.Remoting.Channels.Tcp namespace. The TCP communication can now be secured either through code or the application configuration files. The classes support encryption and authentication using the Security Support Provider Interface (SSPI). It relies internally on the NegotiateStream class to secure the communication.

This article shows how to create and consume .NET remoting clients and servers using Visual Studio 2005 and the Microsoft .NET Framework 2.0, focusing on the security enhancements to the TCP channel and how to use them within your applications. It includes the following examples for communicating over secure TCP and securing the communication both in code and through the configuration file:

  1. Create a remotable object.
  2. Create a server to expose the remotable object.
  3. Create a client to connect to the server and consume the object.
  4. Show an example using the configuration file rather than code to control the security.

All you need to follow along is a familiarity with remoting, which you can acquire by referring to a prior article on the topic.

Create a Remotable Object

A remotable object is nothing more than an object that inherits from MarshalByRefObject. The following sample demonstrates a simple class to expose the omnipresent hello world message as an example. This object exposes a single method HelloWorld that will return a string. Anything that is serializable can be returned from the method call.

The first step is to create a new C# class library project. Add a class called SampleObject and put in the following code. Compile the class to make sure you have everything correct.

using System; namespace CodeGuru.RemotingSample{  /// <remarks>  /// Sample object to demonstrate the use of .NET remoting and IPC.  /// </remarks>  public class SampleObject : MarshalByRefObject   {      /// <summary>      /// Constructor      /// </summary>      public SampleObject()      {      }       /// <summary>      /// Return a hello message      /// </summary>      /// <returns>Hello world message</returns>      public string HelloWorld()      {        return “Hello World!”;      }  }}

Create a Server to Expose the Remotable Object via TCP

To create a server object that will act as a listener to accepts remote object requests, you create an instance of the channel and then register it for use by clients on a specific port. You can register the service as WellKnownObjectMode.SingleCall, which results in a new instance of the object for each client, or as WellKnownObjectMode.Singleton, which results in one instance of the object used for all clients.

For this example, create a server listener. Since the service needs to be bound to an available port, choose a port that you know to be unused on your computer. (The example code uses 8080.) To see a list of the used ports on your computer, open a command prompt and issue the command “netstat –a”. It may produce a long list, so make sure the command prompt buffer sizes are set to allow scrolling. Compile the class to make sure you have everything correct.

Create a new C# console application project. Add a class called SampleRemotingServer and paste in the following code:

using System;using System.Collections;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp; namespace CodeGuru.RemotingSample{  /// <remarks>  /// Sample server to demonstrate the use of secure .NET Remoting.  /// </remarks>  public class SampleRemotingServer  {      public static void Main()       {  // Setup the configuration parameters through a dictionary        IDictionary properties = new Hashtable();        properties.Add(“port”, 8080);        properties.Add(“secure”, true);        properties.Add(“impersonate”, true);         // Create an instance of a channel        TcpServerChannel serverChannel =         new TcpServerChannel(properties, null);        ChannelServices.RegisterChannel(serverChannel);                          // Register as an available service with the name HelloWorld        RemotingConfiguration.RegisterWellKnownServiceType( typeof(SampleObject),                “HelloWorld.rem”, WellKnownObjectMode.Singleton );         Console.WriteLine(“Listening on {0}”, serverChannel.GetChannelUri());  Console.WriteLine(“Press the enter key to exit…”);        Console.ReadLine();      }  }}

Add a reference in your project to System.Runtime.Remoting, otherwise the TcpChannel and other related classes will not be found. In addition, add a reference to the project containing the SampleObject. Otherwise, the code will not compile because it won’t know how to find a reference to SampleObject.

To secure the communication programmatically, you need to configure the server connection. Notice how the example code passes parameter information in through a Dictionary. You can configure a number of additional parameters (Find more parameters in the VS 2005 Beta Docs on MSDN.) The parameters you will use on the server side for this example are as follows:

  • port – specifies the port number on which the channel will listen
  • secure – indicates whether or not the channel is to be secured (When secured, the channel can require user credentials, as well as encrypt and sign the communication. If the TcpServerChannel is secure, then the TcpClientChannel must also be secure. Otherwise, no connection is made.)
  • impersonate – indicates whether or not the server should impersonate the client

Create a TCP Client to Use the Remotable Object

Now that you have your remotable object and a server object to listen for requests, create a client to consume it. The client will be very simple. It will connect to the server, create an instance of the object from the server, and then execute the HelloWorld method.

Create a new C# console application project. Add a class called SampleRemotingClient and paste in the following code:

using System;using System.Collections;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Ipc; namespace CodeGuru.RemotingSample{  /// <remarks>  /// Sample client to demonstrate the use of secure .NET Remoting.  /// </remarks>  public class SampleRemotingClient  {      public static void Main()      {  // Setup the configuration parameters through a dictionary        IDictionary properties = new Hashtable();        properties.Add(“secure”, true);        properties.Add(“connectionTimeout”, 5000);        properties.Add(“tokenImpersonationLevel”, “Impersonation”);          // Create a channel for communicating w/ the remote object        IpcClientChannel clientChannel =         new IcpClientChannel(properties, null);        ChannelServices.RegisterChannel(clientChannel);         // Create an instance of the remote object        RemotingConfiguration.RegisterWellKnownClientType(            typeof(SampleObject),            “ipc://localhost:8080/HelloWorld.rem”);         SampleObject sample = new SampleObject();        Console.WriteLine(“{0}”, sample.HelloWorld());   Console.WriteLine(“Press the enter key to exit…”);        Console.ReadLine();      }   }}

Add a reference to System.Runtime.Remoting in the project, otherwise the TcpChannel and other related classes will not be found. In addition, add a reference to the project containing the SampleObject. Otherwise the code will not compile, because it won’t know how to find a reference to SampleObject. You could use the Activator object to create a remote instance, but this example simply uses the RemotingConfiguration object to register the SampleObject to be created from the remote location.

You also need to pass parameters to the client channel, which you’ll do the same way as the server parameters in the previous section. The parameters you’ll use on the client side are as follows:

  • secure – indicates whether or not the channel is to be secured (When secured, the channel can require user credentials, as well as encrypt and sign the communication. If the TcpServerChannel is secure, then the TcpClientChannel must also be secure. Otherwise no connection is made.)
  • connectionTimeout – dictates number of milliseconds to wait for a successful connection (The default value is to wait indefinitely, which you’ll change for this example.)
  • tokenImpersonationLevel – indicates how the client is to be authenticated with the server

Putting It All Together to Test the IPC Remoting Sample

Once you have created the projects and successfully compiled each of them, you are ready to try it out. Start the server by running the compiled executable. After the server successfully starts, console window will display with the message “Press the enter key to exit…” (see Figure 1).

Figure 1. The Server Successfully Starts

The server is listening, so you are now ready to run the client. Executing the client, whether by running the executable or debugging in Visual Studio, should result in “Hello World!” being displayed in a separate console window (see Figure 2). The window will pause until you press the Enter key in order to allow you time to see the resulting output message.

Figure 2.The Client Successfully Executes

You should change some of the sample code. For example, if you change the client tokenImpersonationLevel to “Identification” and run the code, you’ll receive an exception because the authentication configuration of the client and server do not match.

Using Application Configuration Files

Setting up the security in the application code is all well and good. However, I personally would prefer not to have to change code in order to make modifications to the way the security behaves in communication. This is where the application configuration files come in to the picture. You can accomplish the same thing you did in the parameter setup of both the client and the server through the use of configuration files. You can set up and control the entire connection through configuration files. Just replace the code for configuring and setting up the channel and move it into the configuration files.

RemotingServerSample Configuration File

Add an application configuration file to your server. The following is the App.config file:
<?xml version=”1.0″ encoding=”utf-8″ ?><configuration>  <system.runtime.remoting>    <application>      <service>        <wellknown mode=”Singleton” type=”CodeGuru.RemotingSample.SampleObject, RemotingSample” objectUri=”HelloWorld.rem” />      </service>      <channels>        <channel ref=”tcp” secure=”true” port=”8080″ impersonate=”true” />      </channels>    </application>  </system.runtime.remoting></configuration>

The wellknown element specifies your mode, the object to create, the assembly location, and the URI to assign it. The channel element specifies that you want secure communication, to use port 8080, and to impersonate the user.

The server code becomes much simpler because the channel setup is now pushed into the configuration file:

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp; namespace CodeGuru.RemotingSample{  /// <remarks>  /// Sample server to demonstrate the use of secure .NET Remoting.  /// </remarks>  public class SampleRemotingServer  {      public static void Main()       {  // Set the configuration according to your config file        RemotingConfiguration.Configure(“RemotingClientSample.exe.config”);          Console.WriteLine(“Listening on {0}”, ChannelServices.RegisteredChannels[0].ChannelName);  Console.WriteLine(“Press the enter key to exit…”);        Console.ReadLine();      }  }}

RemotingClientSample Configuration File

Add an application configuration file to your client. The App.config file should contain the following:
<?xml version=”1.0″ encoding=”utf-8″ ?><configuration>  <system.runtime.remoting>    <application>      <channels>        <channel ref=”tcp” secure=”true” tokenImpersonationLevel=”impersonation” />      </channels>    </application>  </system.runtime.remoting></configuration>

The client code becomes much simpler because the channel setup is now pushed into the configuration file:

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Ipc; namespace CodeGuru.RemotingSample{  /// <remarks>  /// Sample client to demonstrate the use of secure .NET Remoting.  /// </remarks>  public class SampleRemotingClient  {      public static void Main()      {  // Set the configuration according to your config file        RemotingConfiguration.Configure(“RemotingClientSample.exe.config”);          // Create an instance of the remote object        RemotingConfiguration.RegisterWellKnownClientType(            typeof(SampleObject),            “ipc://localhost:8080/HelloWorld.rem”);        SampleObject sample = new SampleObject();    Console.WriteLine(“{0}”, sample.HelloWorld());  Console.WriteLine(“Press the enter key to exit…”);        Console.ReadLine();      }   }}

A Step Towards Secure Computing

.NET remoting is a powerful way to enable interprocess communication. The security enhancements to the Runtime.Remoting.Channels.Tcp namespace are a welcome step towards secure computing. It helps to ensure the objects flowing between your applications are secure during transport.

Future Columns

The topic of the next column is yet to be determined. If you have something in particular that you would like to see explained here, contact me at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer (MCSD, MCSE, MCDBA) is a senior architect of .NET applications for large and mid-size organizations. Mark is a technology leader with Crowe Chizek in Indianapolis, Indiana. He leads a group that specializes in architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the second year in a row. You can reach Mark at mstrawmyer@crowechizek.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories