Introduction
Reliability and Security are the two biggest challenges
facing developers and designers of highly available business-critical
distributed applications. Microsoft
Message Queuing (MSMQ) is Microsoft’s solution to reliable, robust distributed
application design.
When I was tasked with developing a secure, reliable WCF
service I turned to Microsoft Message Queuing for the answer. Initially I found
it to be a daunting task and extremely frustrating mainly due to the lack of
documentation (working samples rather than documentation to be honest) and the
concepts were confusing and difficult to grasp initially.
Once I had my service up and running, another challenge was
making the service secure, and that led me towards security certificates, which
itself can be very confusing.
Here I'll try to explain how to implement an MSMQ enterprise
in its simplest form by providing a simplified, step by step tutorial on creating a
client-server application with MSMQ 4.0 and I'll cover the required WCF basics
along the way. In the latter part of the article I'll provide a guide on how to
make our service secure using message based certificate security, which will
encrypt and sign our messages with our self signed keys. I've tried to keep
this article as practical as possible and I won't delve into deep theory (since
it isn't my strong area either). Only a little prior knowledge of WCF is
assumed.
Using the code
I've attached in total 4 solutions with this article. A 'MSMQNoSecurityService' project which is our service project with no security implementations, and a 'MSMQNoSecurityClient' which is the client to consume the service. The other 2 projects, 'MSMQSecuredService' and 'MSMQSecuredClient' are almost identical to the previous 2 projects but they implement the security features which we will discuss later in the article.
The 'MSMQSecuredService' and 'MSMQSecuredClient' are essentially the same service and client that we created without security configurations, and only differ by minor application configuration changes, however I've separated them just for the sake of clarity and convenience. When we arrive at the security discussions later on in the article, you can just make the appropriate changes to your old projects rather than create new ones. So for now, just browse to the 'MSMQ Service With No Security' folder that you downloaded with the article source code.
What is Message Queuing?
When designing distributed applications, selecting a transport protocol is important and it mainly depends on your priorities. If reliability and guaranteed delivery is of utmost importance to your business needs, it is better to opt for a more isolated transport scheme, rather than direct protocols such as HTTP or TCP. In direct transport protocols reliability is difficult to address since all the communication will fail if your network goes down. Queued transport provides more resilience than direct transport. Queued messaging is backed by a repository (the queue), so that if delivery fails, your service can try to send them again after a specified period, in case the connection has been re-established.
WCF provides us with MSMQ, a store-and-forward mechanism which guarantees delivery with message queuing at the Operating System level.
Rather than sending messages directly to a service endpoint, in MSMQ we work against a queue which will act as a repository for messages that were not delivered. In case of failure, MSMQ attempts to contact the receiver periodically until a connection has been established. In short, MSMQ provides us with a great infrastructure to address the aforementioned issues with relative ease.
Getting Started: Creating a Simple WCF Service with MSMQ and no Security
Installing MSMQ Server
Before we can work with Message Queuing, we need to enable it in our Operation System. To do this on windows, go to start->'turn windows features on or off', scroll down the list and select Microsoft Message Queue (MSMQ) Server, expand the node and select all the MSMQ features, we do not need most of the features (we require MSMQ server and MSMQ server core) but there is no harm in activating the other MSMQ features as well. Choose the MSMQ features and select OK. This may take a few minutes as it activates the MSMQ server on your system.
Also make sure you activate MSMQ on any client machines later on when you want to run the samples.
Creating the Service Project
Here we will create 2 projects, a WCF service library project that will host our Service, and a small WPF application as our client which we will host on another computer on our local network. Let's get started.
Start Visual Studio 2010 and run it as Administrator. Create a new WCF Service Library project and call it 'MSMQNoSecurityService'. We see in our solution explorer that Visual Studio has created our Service contract (IService1.cs) and the service implementation for us (Service1.cs). Per WCF basics, the interface class defines our Service and Operation contracts and the Service implements the operation contracts defined in the interface class. Name these 2 files IMSMQService.cs and MSMQService.cs respectively. We will add a simple method (operation) to our service contract called ShowMessage()
which takes in a string argument and doesn’t return anything. For demonstration purposes we will keep the service as simple as possible. Here our ShowMessage()
method will take in a string parameter which is sent to it by the client and prints it to the output console in Visual Studio. Note that when using MSMQ we MUST mark our operation contracts with the [OperationContract(IsOneWay=true)]
attribute since a reliable channel of communication is not guaranteed and we can not expect a reply. So our service contract should look like this now:
namespace MSMQNoSecurityService
{
[ServiceContract]
public interface IMSMQService
{
[OperationContract(IsOneWay = true)]
void ShowMessage(string msg);
}
}
And we need to implement our contract in our service:
namespace MSMQNoSecurityService
{
public class MSMQService : IMSMQService
{
public void ShowMessage(string msg)
{
Debug.WriteLine(msg+" Received at: "+System.DateTime.Now.ToString());
}
}
}
So here we've defined the actual behavior of our ShowMessage()
method. It simply takes in a message and prints it to the output (debug) console along with the time in which it was received. So now that we have our Service contract and implementation, we now need to configure it to use MSMQ.
Creating the Queue
Since MSMQ communication works against a queue, first and foremost we need to create a queue on the machine that will host our service. We can do this programmatically or manually, for the sake of simplicity we will do it manually using the computer management snap in. Click start and search for 'Computer Management'. In Computer Management navigate to the 'Services and Applications' node and expand it, you should now see the 'Message Queuing' node, expand that and select 'Private Queues'.
Now right click the private queues node and select 'New'->'Private Queue'. We'll call our queue testqueue and also ensure it's a transactional queue as is required by MSMQ.
Now if we expand the private queues node we can see our newly created testqueue there, by right clicking and selecting properties we can configure its permissions and see its configuration details.
Configuring our service to use MSMQ transport
Last but definitely not least we need to sepcify netMsmqBinding
in the App.config file of our service. We are mainly interested in the system.serviceModel
section of our service config file, by default it uses wsHttpBinding
, we need to change this to netMsmqBinding
instead.
First and foremost we will change our service name accordingly. Change
<service name="MSMQNoSecurityService.Service1">
to:
<service name="MSMQNoSecurityService.MSMQService">
If we don't provide the correct service name here we will encounter a 'no metadata' error later on when we try to host our service. Next we can change our base address to our liking, this is the address our service advertises itself on the network, by default you have something like:
<add baseAddress = "http://localhost:8732/Design_Time_Addresses/MSMQNoSecurityService/Service1/" />
which will work fine, for the sake of clarity i've changed mine to include the service namespace and name, but this is not mandatory:
<add baseAddress="http://mohammad-pc:8080/Design_Time_Addresses/MSMQNoSecurityService/MSMQService/"/>
Needless to say the initial part of your address will differ to mine based on your computer name. Note that you can also provide an IP address instead of a computer name if your service host computer’s IP is not expected to change. The final critical section of our
App.config that needs fixing is our service endpoint. Service endpoints are important in that they contain several key pieces of configuration information. Actual communication with the service occurs through the endpoints of the service. Endpoints specify an address where they can be found, the binding protocol that describes how a client can communicate with the service endpoint (
BasicHttp, netMsmqBinding, wsHttpBinding, NetTcpBinding
etc.) and also the contract which will show what service operations are available. Now we change our endpoint address to the name of the queue on our computer, since the queue is where messages go through whether or not a connection is established between the client and service.
Naturally we need to specify a
netMsmqBinding
and also the correct service contract name. We've added another property called
bindingConfiguration
which allows us to fine tune our binding and provides us with more configuration options. Your service endpoint should now look like this:
<endpoint address="net.msmq://mohammad-pc/private/testqueue"
binding="netMsmqBinding" bindingConfiguration="MyBinding"
contract="MSMQNoSecurityService.IMSMQService">
Inside the
system.servicemodel
section we need to add a
bindings
section where we can provide the specifics of
MyBinding
<bindings>
<netmsmqbinding>
<binding name="MyBinding">
<security mode="None">
</security></binding>
</netmsmqbinding>
For now we just disable any form of security for our service. Our Final
App.config should look like this:
="1.0"="utf-8"
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="MSMQNoSecurityService.MSMQService">
<host>
<baseAddresses>
<add baseAddress="http://mohammad-pc:8080/Design_Time_Addresses/MSMQNoSecurityService/MSMQService/"/>
</baseAddresses>
</host>
<endpoint address="net.msmq://mohammad-pc/private/testqueue"
binding="netMsmqBinding" bindingConfiguration="MyBinding"
contract="MSMQNoSecurityService.IMSMQService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netMsmqBinding>
<binding name="MyBinding" >
<security mode="None"/>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>
</configuration>
Now we can run our project and
WcfSvcHost
will host our service for us. If you get errors, make sure you're running Visual Studio as Administrator, check your namespaces and your service configuration
App.config file for typos or errors. As depicted below we now have our service up and running, clients on our network can now connect to it and send messages.
Creating the Client Project
Now that we've done the hard part, let’s just create a small client to send messages to our MSMQ service.
Once the service is hosted, on another PC on your network create a WPF application (or a consple app, doesn't really matter) named 'MSMQNoSecurityClient'. Obviously you also need to enable MSMQ server in 'Add or remove windows features' on the client computer as well. Right click 'References' and 'Add service reference...'
In the Address bar type in the address that you gave as your service base address, in my case this was:
http://mohammad-pc:8080/Design_Time_Addresses/MSMQNoSecurityService/MSMQService/
and click 'Go', as shown below Visual Studio will find the service for you, call the service reference 'MSMQServiceReference' in the namespace text box.
If you can't find the service, make sure that it's running on the host computer and that your client machine is connected to the network. Also try removing '/mex' from the last part of your address in the Service Reference Address bar and disabling any firewalls.
Click OK and check the
App.config of your WPF application. You will notice that Visual Studio has conveniently created the bindings for you:
<system.serviceModel>
<bindings>
<netMsmqBinding>
<binding name="NetMsmqBinding_IMSMQService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
deadLetterQueue="System" durable="true" exactlyOnce="true"
maxReceivedMessageSize="65536" maxRetryCycles="2" receiveErrorHandling="Fault"
receiveRetryCount="5" retryCycleDelay="00:30:00" timeToLive="1.00:00:00"
useSourceJournal="false" useMsmqTracing="false" queueTransferProtocol="Native"
maxBufferPoolSize="524288" useActiveDirectory="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport msmqAuthenticationMode="WindowsDomain" msmqEncryptionAlgorithm="RC4Stream"
msmqProtectionLevel="Sign" msmqSecureHashAlgorithm="Sha1" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netMsmqBinding>
</bindings>
<client>
<endpoint address="net.msmq://mohammad-pc/private/TestQueue"
binding="netMsmqBinding" bindingConfiguration="NetMsmqBinding_IMSMQService"
contract="MSMQServiceReference.IMSMQService" name="NetMsmqBinding_IMSMQService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
You can tamper with the various settings in the binding section of your client
App.config later to see the effects, there are various settings regarding how long the client should wait to retry sending messages to the service queue, the maximum size of messages to be sent etc. Note that you need to change these in your service
App.config as well, by default if you specify custom values in your service
App.config file, Visual Studio will create the same settings on your client configuration file when you create a service reference. Now all we need to do is create a client instance and use the operations it exposes to send our messages to the service, go ahead and add these lines in your
MainWindow.xaml.cs file:
using MSMQNoSecurityClient.MSMQServiceReference;
.....
public partial class MainWindow : Window
{
private MSMQServiceReference.MSMQServiceClient client;
public MainWindow()
{
InitializeComponent();
client=new MSMQServiceClient();
}
private void buttonSendMsg_Click(object sender, RoutedEventArgs e)
{
client.ShowMessage(textBox1.Text.ToString());
}
}
All we are doing here is creating an instance of the service reference called 'client', and we take the user input on a text box on our main window and send that to the service's
ShowMessage()
method. So there we have it! We are ready to send messages to our service computer. If you're an astute reader or have prior knowledge of Message queuing you will probably think "but I didn't create a queue on the client machine", and indeed you are correct. Once you've enabled MSMQ server on the client machine, the client will send messages to an outgoing queue on your client machine, so we need not explicitly create a queue on the client machine as well. If you go back and check the 'Message Queuing' node under
'Services and Applications' in computer management, you will see that you have a folder for Outgoing queues, this is what MSMQ uses when a local queue is not specified. Go back and take a look at your client
App.config and look at the endpoint address:
endpoint address="net.msmq://mohammad-pc/private/testqueue"
This is telling your client to send messages to a private queue called
testqueue on mohammad's computer, if MSMQ is unable to connect to that queue it will store those messages in an outgoing queue on itself and re-attempt every x minutes (as specified in your service and client config files).
So go ahead and run both the client and service applications, type a message to send and you will see it arrive in the Visual Studio debug output window (to open this window select View->Output on Visual Studio toolbar and select 'Show output from: Debug').
Message queuing in action
Now the cool part! Disable your network adapter on your client station, but make sure your client application is still running, send some more messages, as expected these messages do not show up on the visual studio output console. Now go to the Computer Management snap in and go to 'Services and Applications' -> 'Message Queuing'->'Outgoing Queues' and you will see the messages stored in an outgoing queue named something like: DIRECT=OS:[ServiceComputerName]\private$\testqueue
You can see the number of messages that you've sent since the connection was lost, and you can see that the state of the queue is 'Waiting to connect'. Turn on the client computers network adapter and keep monitoring the outgoing queue and after a while (as specified in the bindings retryCycleDelay
) the messages will disappear from the outgoing queue and arrive at the Service computer.
If the message can not be delivered to the service host MSMQ will ReceiveRetryCount
(5 in our case) times to deliver the message. If delivery fails in that time, the messages are moved to the retry queue. After retryCycleDelay
minutes (30 minutes in our case), the messages go to the endpoint queue and the delivery is reattempted for the messages. MSMQ will repeat this maxRetryCycle
(2 in our case) times. If the message is still not delivered it will be dealt with according to receiveErrorHandling
in the binding config (fault in our case). Ideally you want to deal with this scenario as messages that stay on the client system accumulate rapidly and take up system resources, these messages are called 'Poison' messages as they are deemed to poison the client computer.
You can read more about the receiveErrorHandling
options here.
So there we have it, a simple WCF service that uses reliable MSMQ communication. Now that we've addressed reliability, in the following section of this article we will look to secure our system with self signed certificates.
Securing our WCF Service with Certificates
Now that we have successfully set up and run our WCF service via MSMQ transport, it would be wise to add some security features to it. We will do this with message based certificate security. Before we do that, lets have a brief overview of digital certificates.
What are Certificates?
Digital certificates are responsible for verifying the identity of the people in the cyber world, that is, is this person who they claim to be. They also secure the data that is being exchanged between the client and server from tampering or eavesdropping, this is done by encrypting the data which requires a unique private key for decryption. Digital certificates contain a variety of identification information such as serial numbers, thumbprints etc.
In our scenario will create two certificates, one for the client and one for the server. The client and server will use the public keys inside our certificates to decode the digital certificates, and verify that the certificates are issued by a trusted certification authority, in this case our computers 'Root Agency'. For example once our client application is sure that the Server is who it says it is, it uses the servers public key and identification information inside the certificate to encrypt the messages.
Ideally in a production environment (such as a website you expect users to access and provide personal details) you should not use self signed certificates, mainly because your users won’t be able to access your website in the first place as their browser tells them it doesn’t recognise the certification authority (CA) and shows them that terrifying red screen. In production scenarios you should look to purchase certificates issued by well knows CAs such as Verisign, Equifax etc. If you have a limited number of users accessing your service, and you know who they are and you are able to issue the self signed certificates to them, then you can use self signed certificates.
Implementing a Simple WCF Service with MSMQ Communication and Certificate Security
Now that our service is up and running, we should look to add some security features. We will do so by creating self signed certificates with 'makecert', these certificates will be used to sign and encrypt our messages. This tool generates X.509 certificates along with a public and private key pair stored in the certificate (.cer) file. The public/priate key pair is also used for generating digital signatures which ensure authenticity.
Creating our Certificates
Open Visual Studio 2010 Command Prompt (under Visual Studio 2010 Tools) as Administrator, type in the following commands:
makecert -pe –n "CN=CertServer" –sr localmachine –ss my –sky exchange -b 01/01/2012 -e 06/06/2012
and:
makecert -pe –n "CN=CertClient" –sr localmachine –ss my –sky exchange -b 01/01/2012 -e 06/06/2012
Type the above commands by hand into the visual studio command prompt, if you copy and paste them you may get a 'too many parameters' error.
The 'makecert' command generates a self signed certificate for us, you can find out more about the options that come with makecert here.
here we are basically creating 2 self signed certificates, one to install on the client machine and one for the server, the computer that will host our service. The CN=xxx
specifies the name of the certificate.
-sr
tells the command prompt to store the certificates on the current machine, -ss
specifies the store location that the certificates will be placed in, my
points to the Personal certificate store, we will explain how to access the certificates in this store in a moment. The other commands: -sky
means that we will use the key for both encryption and key exchange, leaving this out would give an error later on when your client tries to send messages to the service, you'll see such errors as "It is likely that certificate 'x' may not have a private key that is capable of key exchange or the process may not have access rights for the private key." so be sure to include that. The other 2 commands, -b
and -e
specify the validation and expiration date of the certificates respectively. So here our certificate will be valid from Jan 1st 2012 through June 6th 2012. So now we have our self signed certificates which we will use for encryption and signing.
Now that we have created our certificates, let’s use them to secure our MSMQ enterprise. To be able to view the certificates type in 'mmc' in run and press enter. This is the Microsoft Management Console which stores information about installed certificates and trusted Certification Authorities (CAs). Now select File –> Add/Remove Snap-in… and you will see this screen:
So we selected Certificates from the 'Available snap-ins' menu on the right, then 'Add>', 'Computer Account', 'Next', 'Finish' (Local Computer) and finally 'Ok'. Try to learn this routine quickly as you’ll be using it often to manage your certificates. Here we can see the certificates that are installed on our system and the trusted CAs. This is the console we will use to manage our certificates from now on. Alternatively you can just save this console to a convenient location which will spare you from repeating the steps mentioned above every time we want to manage our certificates. To do this select 'File'->'Save As' from the mmc console toolbar and save the console to a location, such as your desktop, and you can just reuse that shortcut from now on.
Telling our Computers to trust us
In the mmc console, on the menu on the left hand side go to 'Console Root'->'Certificates(Local Computer)'->'Personal'->'Certificates' and you can see the 2 certificates 'CertClient' and 'CertServer' that we just created. Double click on one of the certificates, under the 'Certification Path' tab we see 'This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities Store' under the certificate status.
To rectify this what we need to do is tell mmc that Root Agency is indeed a trusted CA, so what we will do is add Root Agency's own certificate to the trusted CAs. To do this, select the root agency certificate (remember that we are now in the certification path tab of one of the 2 certificates we just created and RootAgency is the parent of the CertClient or CertServer certficates) click the 'View Certificate' button as shown below:
Click the Details tab and select the 'Copy to File' option. Select 'Next' twice and make sure you choose
'DER encoded binary X.509(.CER)'->'Next'. Now browse to any location, I will place the certificate (.cer file) on my desktop and call it 'Root Agency'. Confirm the steps by 'Next'->'Finish' and 'Ok'. Now if you look to your output location, in my case my desktop you can see the 'Root Agency.cer' file.
Now we can import the certificate to our 'Trusted Root certification authorities' so we can start using it to secure our service. Right click on the Certificates folder under 'Trusted Root Certification Authorities' then select 'All Tasks'->'Import' from the context menu.
In the Certificate Import Wizard that opens click 'Next', then browse to the Root Agency.cer file you just exported (mine is on my desktop), and then click 'Next' in the following window select the 'Place all certificates in the following store' radio button and browse to the 'Trusted Root Certification Authorities'. Confirm by selecting 'Next' and 'Finish', if you get any security warnings, just select Yes. Now refresh the Personal->Certificates folder, if we check our Personal Certificates in mmc, we can see that it no longer shows us the 'Recognised CA' warning, if you double click either the 'ClientCert' or 'ServerCert' certificates and select the certification path tab, you will see that both the 'Root Agency' and 'ClientCert' certificates are now recognised and under the 'Certificate Status' text box we see 'This Certificate is OK.' as depicted below
Now that we have created and installed our certificates and registered them with our Service computers trusted CA we are now able to secure our MSMQ communication via Certificates.
Creating the Secured WCF Service Project
Now we will re-create the project which will host the same service as our previous project, but this time implementing security. Since our new project is almost identical to the previous one, I won't run through the steps of creating it, but I have created a separate project called 'MSMQSecuredService' just for the sake of clarity, which you can download along with this articles source code above. To configure our service to use security we do not need to change anything in our service contract or implementation. What we need to is configure our service App.config file to use certificate security.
Our new service App.config is identical to our previous one for the most part, but we need to define service behavior and specify what kind of security our binding should use. First of all add a service behavior to your service:
service name="MSMQSecuredService.SecuredMSMQService" behaviorconfiguration="SecurityBehavior"
and also a binding configuration within our endpoint section just like before, but this time our binding configuration, called SecuredBinding
will actually do something.
Our new endpoint:
<endpoint address="net.msmq://mohammad-pc/private/testqueue"
binding="netMsmqBinding" bindingConfiguration="SecuredBinding"
contract="MSMQSecuredService.ISecuredMSMQService">
The binding configuration for
SecuredBinding
:
<binding name="SecuredBinding" exactlyonce="true" receiveerrorhandling="Fault">
<security mode="Message">
<message clientcredentialtype="Certificate">
</message></security>
</binding>
Now this is the main ingredient we need to add to our new project in order to configure it to use security:
<behaviors>
<serviceBehaviors>
<behavior name="SecurityBehavior">
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="True" />
<serviceCredentials>
<serviceCertificate findValue="CertServer" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<clientCertificate>
<certificate findValue="CertClient" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<authentication certificateValidationMode="None" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
So, inside the system.serviceModel
section of the App.config add a serviceBehaviors
section within that, this will describe the behavior that we told our service to use in the opening lines of the App.Config. The behavior configuration is quite self explanatory, we've added a servicecredentials
section, which will naturally tell our service what credentials it requires for the client to be able to consume it. Within the serviceCredentials
section we've configured our service to use the certificate called 'CertServer' in the 'My' store of the computer, this is the personal->certificates store inside mmc where we added our certificates (Certificates(Local Computer)->Personal->Certificates in mmc), if we do not have this certificate our service will give us an error when we attempt to host it.
Below the serviceCertificate
section we've defined our clientCertificate
credentials, here we are telling our service to ask the client to provide a certificate called 'ClientCert' which is also installed on the service host's machine. If either of the certificates are not present on the service host or client computers, you'll get an exception: System.InvalidOperationException: Cannot find the X.509 certificate using the following search criteria...
and we won't be able to host or consume the service. Now try running the Project with the security configurations, it should be up and running like before, next we make the appropriate changes to the client project.
Creating the Secured WCF Client Project
First and foremost before we create our client project, we need to install the Server and Client Certificates 'CertServer and 'CertClient' along with their private keys on the client machine. In order to do this we need to export the certificates and their private keys as a .PFX file and install that .PFX file on the client machine.
Exporting the certificates to the Client computer
Go to the certificate repository in mmc: Certficates(Local Computer)->Personal->Certficates. Right Click the 'CertServer' certificate file and select 'All Tasks'->'Export' and select 'Yes, export the private key' option and select the 'Personal Information Exchange - PKCS #12 (.PFX)' option and click 'Next'.
Note that since we are exporting private keys, and since this is used for decrypting encrypted messages and signing messages we must make sure that it doesn't fall into the wrong hands, as a result the Certificate Export Wizard prompts us to set a password for the .PFX file, go ahead and type in a password, make sure you remember this password as you'll be prompted for it again when you want to install the certificate and its private key on the client machine. Next give your .PFX file a name, I've called mine 'CertServer' again, and I've exported it to my pen drive so I can go ahead and install it on the client machine. Confirm by clicking finish, the CertServer.PFX file is now in your specified output location.
Now we need to install this .PFX file on our client machine.
Run the mmc console on the client computer, navigate to Certificates(Local Computer)->Personal->Certificates and right click the Certificates folder and select 'All Tasks'->'Import...'
Click Next and then browse to the .PFX file that we exported from the Service host machine and click Open and Next. You’ll be prompted for the password you set earlier, so go ahead and enter that and leave the 'Include all extended properties checkbox' ticked. Then select 'Place all certificates in the following store'->’Certificate Store: Personal’. Confirm by clicking Next and finish. So we just imported and installed the 'CertServer' certificate and its private key on the client machine. Do the exact same for 'CertClient' and import and install it (along with its private keys) to the client computer.
Again remember that you need to export the private keys as well, if you don't you will get a nice NotSupportedException: The Private Key is Not Present in the X.509 Certificate
when you try to consume the service. Ok, we're almost done, we've copied and installed both certificates on the client machine, now we just need to create the client Application and configure it to use the certificates, this is very simple. I've created a separate WPF project for the client called 'MSMQSecuredClient' which you can download along with the secured and unsecure service and client projects. Since the changes are only at the App.Config file I won't repeat how to create the client and service reference. Create the client application as before, or make the following changes to its App.Config.
<netMsmqBinding>
<binding name="NetMsmqBinding_ISecuredMSMQService" closeTimeout="00:01:00"
//Same as before....
..
//
/>
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</netMsmqBinding>
</bindings>
In the security node of the binding section, set the security mode to Message
, before we set this to None
. Within the security section we tell the client to provide certificate credentials in order to be able to consume the service, another type of credentials could be Username.
As with the Secured Service configuration we also need to configure a behavior for the client endpoint behaviorConfiguration=endpointCredentialBehavior
and set the DNS to the service certificate, before this was just set to 'localhost'
<identity>
<dns value="CertServer" />
</identity>
And again, just as with the secure service, the most crucial configuration change is defining a behavior that incorporates security features for the client:
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialBehavior">
<clientCredentials>
<clientCertificate findValue="CertClient"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<serviceCertificate>
<defaultCertificate findValue="CertServer" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<authentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
We're configuring the client to look for a certificate called 'CertClient' in its local certificate store 'My'. The defaultCertificate
provides the name and location of the certificate the server uses for authentication and encryption, we need the 'CertServer' certificate on the client computer in order to be able to send messages to the service. If we don't provide the client with the default certificate, we will encounter a InvalidOperationException: The service certificate is not provided for target 'net.msmq://xxx-pc/private/queueName
So there we have it, we've successfully implemented a WCF service with MSMQ communication and we've secured our service with digital certificates. One last point to note is that if you encounter a CryptographicException: 'Keyset does not exist'
on the client machine when you attempt to consume the service, try running the project in visual studio as an administrator since the client application will probably not have the credentials to access the certificates. Try running the service and the client, send some messages to the service and you should see them in visual studio's debug output console as before, except now our messages are signed and encrypted.
Note: When I implemented the 'SecuredMSMQService' project for the first time it took a long time for WcfSvcHost to host it, if this is the case with you as well then be patient, try stopping and running the project again.
Conclusion
In this article we explained how to set up a simple WCF service with MSMQ communication and consumed it with a small client application. We walked through the steps required to enable message queuing in windows and explained the concepts of message queuing. Digital certificates were briefly discussed and an overview on creating self signed certificates was also given and we discussed how to secure our service with self signed certificates.