July 2009

Volume 24 Number 07

Foundations - Securing The .NET Service Bus

By Juval Lowy | July 2009


Configuring Authentication
CardSpace Authentication
Password Authentication
Certificate Authentication
No Authentication
Transfer Security
Transport Security
Message Security
TCP Relay Binding and Transfer Security
WS Relay Binding and Transfer Security
One-Way Relay Binding and Transfer Security
Streamlining Transfer Security

In my April 2009 column, I presented the .NET Services Bus and described how you can utilize relay bindings to connect your application and customers across almost all network boundaries. However, if just anyone were allowed to relay messages to your service, or if any service could receive your client calls, the relay service would be a dangerous proposition. You need to protect the transfer of the message from the client to the service via the relay service. In this column, I show you how to secure the .NET Services Bus and also provide some helper classes and utilities to automate many of the details.

The .NET Services Bus mandates that the service must always authenticate itself to receive relayed messages. Clients, on the other hand, may or may not authenticate themselves. Typically (and by default), clients do authenticate, but the relayed service may decide to waive the client's .NET Services Bus authentication.

The .NET Services Bus offers three different authentication mechanisms—CardSpace, password, or certificate—and it is up to the solution administrator to associate these using the solution page shown in Figure 1.

Figure 1

Figure 1 Configuring Solution Authentication Options

A single solution can support multiple authentication options, and for each option the administrator can add multiple credentials. For example, the administrator might configure three passwords, two cards, and a single certificate. Presenting any one of these credentials is enough to authenticate against the relay service. Also, the service and the client can use different authentication methods. For example, the service can use a password, and the client a certificate.

Configuring Authentication

The enum TransportClientCredentialType, shown here, represents the available credential options:

public enum TransportClientCredentialType { CardSpace, UserNamePassword, X509Certificate Unauthenticated, FederationViaCardSpace, AutomaticRenewal }

In TransportClientCredentialType, Client refers to a client of the .NET Services Bus—that is, both the client and the relayed service.

The preferred authentication mechanism and the credentials themselves are configured using an endpoint behavior called TransportClientEndpointBehavior, defined in Figure 2.

Figure 2 TransportClientEndpointBehavior

public class TransportClientEndpointBehavior: IEndpointBehavior { public TransportClientCredentials Credentials { get; } public TransportClientCredentialType CredentialType { get; set; } } public class TransportClientCredentials { public X509CertificateCredential ClientCertificate { get; } public UserNamePasswordCredential UserName { get; } }

Using an endpoint behavior (as opposed to a service behavior) provides two advantages. First, a service host can choose a different authentication mechanism for each endpoint. Second, an endpoint behavior offers a unified programming model for the client and the service because the client has only endpoint behaviors.

CardSpace Authentication

CardSpace is the default credential type used by all relay bindings. When the client or the host uses CardSpace authentication, the user is prompted to provide a card on opening either the host or the proxy. After the user provides the card, it is bundled in the connection request message to the relay service. Obviously, such an approach is best suited for interactive applications. These prompts, however, will annoy the user if they occur every time the user opens a new proxy or host. Fortunately, the credential is cached in the app domain, and the user is not prompted again as long as he or she accesses the same endpoint address.

Password Authentication

Like CardSpace authentication, password authentication is best suited for an interactive application, typically in conjunction with a login dialog box. However, there is no need to prompt the user to enter a user name because that is always the solution name. After the user provides the password, you need to programmatically provide the solution name and the password to the TransportClientEndpointBehavior.

On the host side, you need to instantiate a new TransportClientEndpointBehavior object and set the CredentialType property to TransportClientCredentialType.UserNamePassword. The credentials themselves are provided to the Credentials property. You then add this behavior to every endpoint of the host that uses the relay service, as shown in Figure 3.

Figure 3 Providing the Host with the Solution Password

TransportClientEndpointBehavior behavior = new TransportClientEndpointBehavior(); behavior.CredentialType = TransportClientCredentialType.UserNamePassword; behavior.Credentials.UserName.UserName = "MySolution"; behavior.Credentials.UserName.Password = "MyPassword"; ServiceHost host = new ServiceHost(typeof(MyService)); foreach(ServiceEndpoint endpoint in host.Description.Endpoints) { endpoint.Behaviors.Add(behavior); } host.Open();

You can encapsulate and automate the steps in Figure 3by using extension methods such as the SetServiceBusPassword methods of my ServiceBusHelper static class, shown here:

public static class ServiceBusHelper { public static void SetServiceBusPassword(this ServiceHost host, string password); public static void SetServiceBusPassword(this ServiceHost host, string solution, string password); }

Using these extensions, Figure 3is condensed to the following:

ServiceHost host = new ServiceHost(typeof(MyService)); host.SetServiceBusPassword("MyPassword"); host.Open();

You can see an implementation of the SetServiceBusPassword methods without error handling in the sample code that accompanies this article.

ServiceBusHelper defines the helper private method SetBehavior, which accepts a collection of endpoints and assigns a provided TransportClientEndpointBehavior object to all endpoints in the collection. The private SetServiceBusPassword helper methods accept a collection of endpoints, the solution password, and optionally the solution name. If the solution name is not specified, SetServiceBusPassword extracts it from the address of the first endpoint. SetServiceBusPassword then creates a TransportClientEndpointBehavior, configures it to use the password, and calls SetBehavior. The public SetServiceBusPassword methods simply call the private ones with the collection of endpoints of the host.

The client needs to follow similar steps, except there is only one endpoint to configure—the one the proxy is using, as shown in Figure 4.

Figure 4 Setting the Solution Password on the Proxy

TransportClientEndpointBehavior behavior = new TransportClientEndpointBehavior(); behavior.CredentialType = TransportClientCredentialType.UserNamePassword; behavior.CredentialType = TransportClientCredentialType.UserNamePassword; behavior.Credentials.UserName.UserName = "MySolution"; behavior.Credentials.UserName.Password = "MyPassword"; MyContractClient proxy = new MyContractClient(); proxy.Endpoint.Behaviors.Add(behavior); proxy.MyMethod(); proxy.Close();

Again, you should encapsulate this repetitive code with extension methods and offer similar support for working with class factories. Using these extensions, Figure 4is condensed to this code:

MyContractClient proxy = new MyContractClient(); proxy.SetServiceBusPassword("MyPassword"); proxy.MyMethod(); proxy.Close();

Figure 5 shows the implementation of two of the client-side SetServiceBusPassword<T> extensions. Note the use of the SetServiceBusPassword helper methods by wrapping the single endpoint the proxy has with a collection of endpoints.

Figure 5 Implementing SetServiceBusPassword<T>

public static class ServiceBusHelper { public static void SetServiceBusPassword <T> (this ClientBase <T> proxy, string password) where T: class { if (proxy.State == CommunicationState.Opened) { throw new InvalidOperationException("Proxy is already opened"); } proxy.ChannelFactory.SetServiceBusPassword(password); } public static void SetServiceBusPassword <T> (this ChannelFactory <T> factory, string password) where T: class { if (factory.State == CommunicationState.Opened) { throw new InvalidOperationException("Factory is already opened"); } Collection < ServiceEndpoint > endpoints = new Collection <ServiceEndpoint> (); endpoints.Add(factory.Endpoint); SetServiceBusPassword(endpoints, password); } //More members }

Because TransportClientEndpointBehavior is just another endpoint behavior, you can also configure it in the config file. Storing the password in a text config file, however, is highly inadvisable.

Certificate Authentication

Using a certificate to authenticate against the .NET Service Bus is the best option for noninteractive applications, and you can set the certificate both programmatically and in the config file.

The main hurdle in using certificates is that the certificate must contain a private key, which entails an elaborate setup sequence by the solution administrator. However, once the solution is configured to use the certificate, the rest of the work is straightforward.

Both the client and the service host use identical configuration entries, as shown in Figure 6.

Figure 6 Solution Certificate in Config

<endpoint behaviorConfiguration = "RelayCert" ... /> ... <behaviors> <endpointBehaviors> <behavior name = "RelayCert"> <transportClientEndpointBehavior credentialType = "X509Certificate"> <clientCredentials> <clientCertificate findValue = "MyRelayCert" storeLocation = "LocalMachine" storeName = "My" x509FindType = "FindBySubjectName" /> </clientCredentials> </transportClientEndpointBehavior> </behavior> </endpointBehaviors> </behaviors>

To programmatically provide the certificate on the host side, you need to follow steps similar to those shown earlier in Figure 3. Besides setting the credentials type to TransportClientCredentialType.X509Certificate, you need to use the SetCertificate method of the ClientCertificate property on Credentials. Here again, you can streamline it using host extension methods, as shown here:

public static class ServiceBusHelper { public static void SetServiceBusCertificate(this ServiceHost host); public static void SetServiceBusCertificate(this ServiceHost host, string subjectName); public static void SetServiceBusCertificate(this ServiceHost host, object findValue, StoreLocation location, StoreName storeName, X509FindType findType); //More members }

For example:

ServiceHost host = new ServiceHost(typeof(MyService)); host.SetServiceBusCertificate("MyRelayCert"); host.Open();

SetServiceBusCertificate defaults the store to MyComputer and the location to My, and it looks up the certificate by name. SetServiceBusCertificate also extracts the solution name from the host endpoints.

The client can also programmatically provide the certificate to use to the proxy by following steps similar to Figure 4, and you can automate this by using these extension methods, which follow the same default values discussed for the host:

public static class ServiceBusHelper { public static void SetServiceBusCertificate <T> (this ClientBase <T> proxy) where T: class; public static void SetServiceBusCertificate <T> (this ClientBase <T> proxy, string subjectName) where T: class; public static void SetServiceBusCertificate <T> (this ClientBase <T> proxy, object findValue, StoreLocation location, StoreName storeName, X509FindType findType) where T: class; //Similar methods for a channel factory }

Using the extensions on the client side yields this code:

MyContractClient proxy = new MyContractClient(); proxy.SetServiceBusCertificate("MyRelayCert"); proxy.MyMethod(); proxy.Close();

No Authentication

Although the service must always authenticate against the service bus, you might decide to exempt the client and allow it unauthenticated access to the relay service. In that case, the client must set TransportClientEndpointBehavior to TransportClientCredentialType.Unauthenticated. When the clients are not authenticated by the relay service, it is now up to the relayed service to authenticate the clients. The downside is that the service is now less shielded than when the relay service authenticated clients. In addition, you must use message security to transfer the client credentials (as discussed later). To enable unauthenticated access by the client, both the service and the client must explicitly allow it by configuring the relay binding to not authenticate, using the enum RelayClientAuthenticationType, shown here:

public enum RelayClientAuthenticationType { RelayAccessToken, //Default None }

You assign that enum via the Security property.

Transfer Security

The next crucial aspect of security is how to securely transfer the message through the relay to the service. In addition to message transfer security, another important design decision is which client credentials (if any) the message should contain. Transfer security is independent of how the client or the service authenticates itself against the .NET Service Bus.

The .NET Services Bus offers four options for transfer security, represented by the enum EndToEndSecurityMode:

public enum EndToEndSecurityMode { None, Transport, Message, TransportWithMessageCredential //Mixed }

The four options are None, Transport, Message, and Mixed. None means just that—the message is not secured at all. Transport uses SSL or HTTPS to secure the message transfer. Message security encrypts the body of the message so that it can be sent over nonsecured transports. Mixed uses message security to contain the client's credentials but transfers the message over a secured transport. Figure 7shows the way the relay binding supports the various transfer security modes and their default values. A bold plus sign (+) marks defaults.

Figure 7 Binding and Transfer Security

Binding None Transport Message Mixed
TCP (Relayed) + + + +
TCP (Direct/Hybrid) + - + -
WS + + + +
One-Way + + + -

You configure transfer security in the binding. Although the relay bindings use different default values, all the relay bindings offer at least one constructor that takes EndToEndSecurityMode as a construction parameter. You can also configure transfer security after construction by accessing the Security property and its Mode property.

Transport Security

When it comes to transfer security, transport security is the simplest to set up and configure. When using transport security, all client calls are anonymous—the client messages do not contain any client credentials. While transport security is the easiest to use, it does not provide end-to-end security. It secures only the transfer of the message to the relay service and from the relay service. The journey inside the relay service is not secured.

This means that in theory, the relay service can eavesdrop on the communication between the client and the service and even tamper with the messages. However, I believe that in practice this is impractical given the volume of traffic to the .NET Services Bus. Simply put, this kind of subversion cannot be performed as an aside and requires dedicated resources, planning, staff, and technology. In addition, Microsoft has proven over the years that it has the highest integrity and respect for its customers' privacy, and it has many other areas it could have abused if it were indeed malicious.

Message Security

Message security encrypts the body of the message using a service-provided certificate. Because the message itself is protected rather than the transport, the journey inside the relay is protected as well. The downside to message security is that it requires additional setup steps.

While I think that in practice transport security is enough, it is vital to assure customers and users of the presence of end-to-end privacy and integrity and to guard against even theoretical compromises. I therefore recommend always relying on message security for all relayed communication, which will also provide additional benefits, such as direct connection and the availability of security call context to the service.

Unlike in transport security, in message security the message might contain the client's credentials. The primary use of client credentials by the service is for local authorization of the call to establish some role-based security policy. Whenever the message contains credentials, the service must also authenticate them (even if all it wants is to authorize the client). Note that such authentication is on top of the authentication the relay service has already performed. If the relay service has already authenticated the client, authenticating the call again by the service does not add much in the way of security, yet it burdens the service with managing the client's credentials. If the relay service is not authenticating the client, the service will be subjected to all the unwanted traffic of unauthenticated clients, which could have severe IT operations implications.

For these reasons, I find that the best practice is for the relay service to authenticate the client and to avoid having the service do it again. You should also design your service so that it has no need for the client's credentials. Such a design is aligned with the chain-of-trust design pattern that works well in a layered architecture. That said, there are cases when the service needs the client credentials for a local use other than authorization, such as personalization, auditing, or proprietary integration with legacy systems.

TCP Relay Binding and Transfer Security

The TCP relay binding defaults to transport security, and no special configuration steps are required. It simply uses SSL over port 828. When using transport security, however, you can only use the TCP relay binding connection mode of TcpRelayConnectionMode.Relayed.

Because the call is anonymous, on the service side Windows Communication Foundation (WCF) attaches a generic principal with a blank identity to the thread executing the call, and the ServiceSecurityContext is null.

To protect the transfer of the message, you must configure the service host with a certificate. The client will by default negotiate the certificate (obtain its public key), so there is no need to explicitly list the certificate in the client's config file. However, the client still needs to validate the negotiated certificate. As with regular WCF and message security, the best practice is to validate the certificate using peer-trust, which means installing the certificate beforehand in the client's Trusted People folder. Besides providing true end-to-end transfer security over the relay, using message security also enables the use of the direct and hybrid connection modes.

As discussed previously, the message might or might not contain the client's credentials. If you avoid sending the credentials in the message, WCF will attach to the thread executing the call a Windows principal with a blank identity, which does not make much sense. When using message security without credentials, you should also set the host PrincipalPermissionMode to None to get the same principal as with transport security. To configure the binding for message security with anonymous calls, use MessageCredentialType.None and assign that value to the ClientCredentialType property of MessageSecurityOverRelayConnection, available in the Message property of NetTcpRelaySecurity. Figure 8shows code that demonstrates this.

Figure 8 Configuring a Binding for Message Security with Anonymous Calls

public sealed class NetTcpRelaySecurity { public EndToEndSecurityMode Mode { get; set; } public MessageSecurityOverRelayConnection Message { get; } //More members } public sealed class MessageSecurityOverRelayConnection { public MessageCredentialType ClientCredentialType { get; set; } //More members }

Figure 9 shows the required host-side config file.

Figure 9 Configuring the Host for Message Security

<service name = "..." behaviorConfiguration = "MessageSecurity"> <endpoint ... binding = "netTcpRelayBinding" bindingConfiguration = "MessageSecurity" /> </service> ... <serviceBehaviors> <behavior name = "MessageSecurity"> <serviceCredentials> <serviceCertificate findValue = "MyServiceCert" storeLocation = "LocalMachine" storeName = "My" x509FindType = "FindBySubjectName" /> </serviceCredentials> <serviceAuthorization principalPermissionMode ="None"/> </behavior> </serviceBehaviors> <bindings> <netTcpRelayBinding> <binding name = "MessageSecurity"> <security mode = "Message"> <message clientCredentialType = "None"/> </security> </binding> </netTcpRelayBinding> </bindings>

On the client side, you must include the service certificate name in the address identity of the endpoint because that name does not match the relay service domain. Figure 10shows the required config file.

Figure 10 Configuring the Client for Message Security

<client> <endpoint behaviorConfiguration = ";ServiceCertificate"; binding = ";netTcpRelayBinding"; bindingConfiguration = ";MessageSecurity"; <identity> <dns value = ";MyServiceCert";/> </identity> ... </endpoint> </client> <bindings> <netTcpRelayBinding> <binding name = ";MessageSecurity";> <security mode = ";Message";> <message clientCredentialType = ";None";/> </security> </binding> </netTcpRelayBinding> </bindings> <behaviors> <endpointBehaviors> <behavior name = ";ServiceCertificate";> <clientCredentials> <serviceCertificate> <authentication certificateValidationMode= ";PeerTrust";/> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors>

If you want to include the client credentials in the message, the service must also authenticate those credentials, using the same setting as with regular TCP calls. In that case, the service principal and primary identity will both have an identity matching those credentials. The credential can be a user name and password, a certificate, or an issued token. You must indicate to both the host and the client in the binding which credential types you expect. For example, for user name credentials, use the following:

<bindings> <netTcpRelayBinding> <binding name = "MessageSecurity"> <security mode = "Message"> <message clientCredentialType = "UserName"/> </security> </binding> </netTcpRelayBinding> </bindings>

On the host side, if the credentials are user name and password, you must also configure, using behaviors, how to authenticate and authorize the credentials. The default will be Windows credentials, but the more common choice would be using some credentials store such as the ASP.NET providers:

<service name = "..." behaviorConfiguration = "CustomCreds"> ... </service> ... <serviceBehaviors> <behavior name = "CustomCreds"> <serviceCredentials> <userNameAuthentication userNamePasswordValidationMode = "MembershipProvider" /> </serviceCredentials> <serviceAuthorization principalPermissionMode = "UseAspNetRoles"/> </behavior> </serviceBehaviors>

The client has to populate the proxy with the credentials. When using a username and password, the client code would be insert like this:

MyContractClient proxy = new MyContractClient(); proxy.ClientCredentials.UserName.UserName = "MyUserName"; proxy.ClientCredentials.UserName.Password = "MyPassword"; proxy.MyMethod(); proxy.Close();

The client has no way of knowing if the credentials it provides are authenticated on the service side as Windows or custom credentials.

Mixed transfer security is the only way to avoid anonymous calls over transport security. Since transport security cannot pass credentials, you pass the credentials using message security, hence the term mixed. When using mixed transfer security over the TCP relay binding you are restricted to using only relayed connections.

Figure 11 shows how to configure either the service or the client for mixed security.

Figure 11 Configuring for Mixed Security

<endpoint binding = "netTcpRelayBinding" bindingConfiguration = "MixedSecurity" ... /> ... <bindings> <netTcpRelayBinding> <binding name = "MixedSecurity"> <security mode = "TransportWithMessageCredential"/> </binding> </netTcpRelayBinding> </bindings>

After the messages are received by the service, the host must authenticate the calls as with regular TCP. Once authenticated, the service call will have a principal object matching the credentials provided and a security call context.

WS Relay Binding and Transfer Security

Combining the WS binding with transport security is as easy as changing the address schema from HTTP to HTTPS and setting the binding to use transport security, as shown in Figure 12.

Figure 12 WS Relay Binding with Transport Security

<endpoint address = "https://MySolution.servicebus.windows.net/..." binding = "wsHttpRelayBinding" bindingConfiguration = "TransportSecurity" ... /> <bindings> <wsHttpRelayBinding> <binding name = "TransportSecurity"> <security mode = "Transport"/> </binding> </wsHttpRelayBinding> </bindings>

Note that the WS relay binding defaults to using message security for transfer security. Since message security requires additional configuration steps, the WS relay binding does not work as-is out of the box, and all calls will fail. However, configuring the WS relay binding to use message (or mixed) security is identical to configuring the TCP relay binding.

One-Way Relay Binding and Transfer Security

The one-way relay binding (and its subclasses) is the only binding that defaults to having no transfer security at all. In addition, it does not support mixed transfer security. Configuring it to use transport security is the same as with the TCP and WS relay bindings. Configuring it to use message security is similar but with one important difference—the one-way relay binding cannot negotiate the service certificate because there may not even be a service, and no direct interaction with the service takes place. When using message security on the client, you must explicitly specify the service certificate, as shown in Figure 13.

Figure 13 One-Way Relay Binding with Message Security

<client> <endpoint behaviorConfiguration = "ServiceCertificate" ... </endpoint> </client> <behaviors> <endpointBehaviors> <behavior name = "ServiceCertificate"> <clientCredentials> <serviceCertificate> <scopedCertificates> <add targetUri = "sb://MySolution.servicebus..." findValue = "MyServiceCert" storeLocation = "LocalMachine" storeName = "My" x509FindType = "FindBySubjectName" /> </scopedCertificates> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors>

Another important distinction between the one-way relay binding and the other relay bindings is that if the call is anonymous, with either transport or message security, the call has a security call context whose primary identity is

service bus certificate CN=servicebus.windows.net.

Streamlining Transfer Security

While transfer security offers a slew of details and intricate options, you can and should streamline and automate most of these security configuration decisions. To encapsulate it on the host side, use my ServiceBusHost class, defined as insert the following:

public class ServiceBusHost: ServiceHost { public ServiceBusHost(object singletonInstance, params Uri[] baseAddresses); public ServiceBusHost(Type serviceType, params Uri[] baseAddresses); public void ConfigureAnonymousMessageSecurity(string serviceCert); public void ConfigureAnonymousMessageSecurity(string serviceCert, StoreLocation location, StoreName storeName); public void ConfigureAnonymousMessageSecurity(StoreLocation location, StoreName storeName, X509FindType findType, object findValue); //More members }

When using ServiceBusHost, no other setting in config or in code is required. Per my recommendation, you can use the ConfigureAnonymousMessageSecurity method to enable anonymous calls over message security. All you need to provide it is the certificate name to use:

ServiceBusHost host = new ServiceBusHost(typeof(MyService)); host.ConfigureAnonymousMessageSecurity("MyServiceCert"); host.Open();

ConfigureAnonymousMessageSecurity will default the certificate location to the local machine and the certificate store to My, and it will look up the certificate by its common name. If you do not call ConfigureAnonymousMessageSecurity, ServiceBusHost will default to using anonymous message security with the solution name for the certificate name:

ServiceBusHost host = new ServiceBusHost(typeof(MyService)); host.Open();

You can also use the overloaded versions that let you explicitly specify some or all of the certificate details.

ServiceBusHost makes use of the ConfigureBinding method of ServiceBusHelper. ConfigureBinding defaults to anonymous calls. If the calls are to have credentials, ConfigureBinding always uses username credentials. With the TCP relay binding, ConfigureBinding uses the hybrid connection mode. ConfigureBinding also always enables reliable messages.

ServiceBusHost also supports message security with credentials via the ConfigureMessageSecurity methods:

public class ServiceBusHost: ServiceHost { public void ConfigureMessageSecurity(); public void ConfigureMessageSecurity(string serviceCert); public void ConfigureMessageSecurity(string serviceCert, string applicationName); public void ConfigureMessageSecurity(string serviceCert, bool useProviders, string applicationName); //More members }

ConfigureMessageSecurity defaults to using the ASP.NET membership providers, but it can be instructed to use Windows accounts as well. The implementation of ConfigureMessageSecurity is similar to that of ConfigureAnonymousMessageSecurity.

You can provide clients with an easy way of configuring message security with my ServiceBusClientBase<T>, defined as

public abstract class ServiceBusClientBase <T> : ClientBase <T> where T: class { public ServiceBusClientBase(); public ServiceBusClientBase(string endpointName); public ServiceBusClientBase(Binding binding, EndpointAddress remoteAddress); public ServiceBusClientBase(string username, string password); public ServiceBusClientBase(string endpointName, string username, string password); public ServiceBusClientBase(Binding binding, EndpointAddress address, string username, string password); protected virtual void ConfigureForServiceBus(); protected virtual void ConfigureForServiceBus(string username, string password); }

ServiceBusClientBase<T> offers two sets of constructors. The constructors that merely take the endpoint parameters all default to using message security with anonymous calls. You can also use the constructors that accept the username and password credentials. If no endpoint address identity is provided, ServiceBusClientBase<T> defaults it to the solution name. You use ServiceBusClientBase<T> like the WCF-provided ClientBase<T>:

[ServiceContract] interface IMyContract { [OperationContract] void MyMethod(); } class MyContractClient: ServiceBusClientBase <IMyContract>, IMyContract { public MyContractClient() {} public void MyMethod() { Channel.MyMethod(); } }

The sample code download includes the full implementation of ServiceBusClientBase<T>. ServiceBusClientBase<T> uses peer-trust to validate the service certificate. The bulk of the work is done by passing the endpoint binding to ServiceBusHelper.ConfigureBinding.

The one remaining sore point is the one-way relay binding, with its lack of certificate negotiation. To alleviate that, I wrote OneWayClientBase<T>:

public abstract class OneWayClientBase<T> : ServiceBusClientBase<T> where T : class { //Same constructors as ServiceBusClientBase<T> public void SetServiceCertificate(string serviceCert); public void SetServiceCertificate(string serviceCert, StoreLocation location, StoreName storeName); public void SetServiceCertificate(object findValue, StoreLocation location,StoreName storeName,X509FindType findType); }

OneWayClientBase<T> derives from ServiceBusClientBase<T> and adds the SetServiceCertificate methods. If you never call SetServiceCertificate, OneWayClientBase<T> simply looks up the service certificate from config. SetServiceCertificate offers a simple programmatic way of avoiding the config altogether. It even sets the identity tag of the endpoint address. SetServiceCertificate uses the same defaults as ServiceBusHost, including using the solution name for the certificate name if no certificate is provided. Here's how you use OneWayClientBase<T> :

class MyContractClient: OneWayClientBase <IMyContract> , IMyContract { public MyContractClient() {} public void MyMethod() { Channel.MyMethod(); } } MyContractClient proxy = new MyContractClient(); proxy.SetServiceCertificate("MyServiceCert"); proxy.MyMethod(); proxy.Close();

As you can see, using OneWayClientBase<T> is straightforward.

Send your questions and comments to mmnet30@microsoft.com.

Juval Lowy a software architect with IDesign providing WCF training and architecture consulting. His recent book is Programming WCF Services 2nd Edition(O'Reilly, 2008). He is also the Microsoft Regional Director for the Silicon Valley. Contact Juval at www.idesign.net.