Wout Ware Blog

My external memory

How to reset the domain administrator password on Windows Server 2008 with just the Windows Installation Disk

clock April 8, 2010 01:02 by author Administrator

I had to reset the domain administrator password on Windows Server 2008 because the password was lost (not by me for a change!). I ran across this very nice youtube video by Mathieu (Chateau?) that worked perfectly: http://www.youtube.com/watch?v=Ar-VoO9ogHc.

I'm kinda paranoid about useful webpages being removed, so I am writing down a description of the video here just in case:

  • Insert the original Windows Server 2008 installation DVD.
  • Reset the computer, boot from DVD.
  • Choose option "Repair your computer".
  • Start command prompt.
  • C:\
  • CD C:\Windows\System32
  • MOVE Utilman.exe Utilman.exe.bak
  • COPY Cmd.exe Utilman.exe
  • Restart windows
  • At the login screen, type Windows-U, this fires up cmd.exe now
  • NET USER administrator <new password>
  • Presto! Login as administrator!
  • Now repair computer again and move the original Utilman.exe back.

There are a bunch of commercial tools to reset the administrator password, but there is no need for it! Furthermore most of them seem to reset just the local administrator account's password, rather than the domain administrator password.



vsdiag_regwcf.exe

clock February 23, 2010 01:46 by author Administrator

Note to self: don't forget to run this command:

"%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\vsdiag_regwcf.exe" -u

when getting the annoying error popup in VisualStudio:

---------------------------
Microsoft Visual Studio
---------------------------
Unable to automatically debug 'Xyz'. The remote procedure could not be debugged. This usually indicates that debugging has not been enabled on the server. See help for more information.
---------------------------
OK   Help  
---------------------------

 



File change notifications not happening on VirtualBox

clock February 21, 2010 08:04 by author Administrator

I recently started using VirtualBox as a replacement for VirtualPC 2007 to do builds using an old build environment. The NUnit test was failing, so I fixed the problem, compiled, and reran the test. Hmmm, still failing! Retried a couple of times, and then it hit me! NUnit didn't see the changed binaries when in VirtualBox. So I restarted NUnit, and presto! Tests are running again! Now I hope I won't forget next time...



Use .svc extension in address when hosting using ASP.NET server (e.g. in VS "WCF Service Application" project)

clock December 26, 2009 08:49 by author Administrator

Today I was playing with WCF and for a change I created a "WCF Service Application" project to act as a WCF service. I removed the default created stuff and added my own service class. I quickly whipped up a client project, and fired up the debugger:

+        [System.ServiceModel.CommunicationException]    {"An error occurred while receiving the HTTP response to http://localhost:8083/Service. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."}    System.ServiceModel.CommunicationException

Argh! Why me? A couple of hours, a lot of cursing and trial and erroring later I figured out that the ASP.NET development server must be able to recognize the .svc extension. So the client configuration must refer to the actual .svc file:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <client>
            <endpoint address="http://localhost:8083/Service.svc" binding="wsHttpBinding" bindingConfiguration="" contract="WcfData.IService"
                name="MyService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

 

The service's config may have the address attribute empty or the explicit reference to the .svc file, but I had the string "http://localhost:8083/Service" initially, which works ok with anything else but ASP.NET hosts. Hosting in a Console app, or windows service using ServiceHost works just fine without .svc extension.



Implementing IErrorHandler and working around SerializationException.

clock December 17, 2009 06:40 by author Administrator

EDIT Nov 25, 2009: note that the problem described in this post below only happens in the VS debugger, under some circumstances.

Today I was attempting to implement a custom IErrorHandler to wrap Exception instances into a FaultException<T> instance, so they can be properly turned into a soap fault by WCF. Seemed easy at first, but there was a nasty fly in the ointment. The original code in the service would work fine, which looked like this:

throw new FaultException<Exception>(new Exception("test"));


When I would just throw a new Exception("test") in the service, and wrap it in the error handler, then it wouldn't work. The service code now looks like this:

throw new Exception("test");


The error handler code looks like this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace Test {
    public class ErrorHandler : IErrorHandler {
        #region IErrorHandler Members

        public bool HandleError(Exception error) {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
            if (error is FaultException) {
                return;
            }

            Type faultExceptionType = typeof(FaultException<>).MakeGenericType(error.GetType());
            FaultException faultException = (FaultException)Activator.CreateInstance(faultExceptionType, error);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(
                version,
                messageFault,
                faultException.Action
            );
        }

        #endregion
    }
}


Now the exception doesn't get turned into a soap fault anymore, instead we're getting a SerializationException hidden away quite nicely. What's going on???

After a whole day of digging around, googling, debugging and trying hacks I did manage to find out what's going on and also find a workaround. Googling around first made me find part of the problem. The normal DataContractSerializer can't handle the Exception.Data property when it has a value. And when the exception arrives at the error handler, it does have a Data value. This isn't the complete story though, the field behind the Data property is _data. Whenever Data gets evaluated, it will set _data to a value, and then the serialization breaks. The next piece in the puzzle is this: initially the _data field of an Exception is null, but doing a throw/catch causes the _data to be initialized (Edit Nov 25, 2009: only when the debugger stops when e.g. the exception is thrown, when the debugger doesn't stop, _data remains null and there is no problem). In other words: a throw/catch will break serialization! Note that this all is a debugger side effect, so once you've figured out it's a debugger side effect, you can just make sure you don't break on exceptions in the service.

There's also a workaround. The _data field can be set to null in the error handler using reflection. Modify the ProvideFault method like this and the serialization will work again:

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
            if (error is FaultException) {
                return;
            }

            // This hack sets the _data field to null again.
            FieldInfo fieldInfo = typeof(Exception).GetField("_data", BindingFlags.Instance | BindingFlags.NonPublic);
            fieldInfo.SetValue(error, null);

            Type faultExceptionType = typeof(FaultException<>).MakeGenericType(error.GetType());
            FaultException faultException = (FaultException)Activator.CreateInstance(faultExceptionType, error);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(
                version,
                messageFault,
                faultException.Action
            );
        }

 



Note to self: explore authentication using BasicHttpBinding and OperationContext.

clock October 1, 2009 21:50 by author Administrator

Just a note to remind myself to dig into this next week: this very nice article shows how to do user authentication using BasicHttpBinding and OperationContext: http://www.netfxharmonics.com/2008/11/Understanding-WCF-Services-in-Silverlight-2. One can stuff the username/password in the message headers, so you can actually access them from within your service method from the OperationContext.Current. The approach I tried before using a custom UserNamePasswordValidator has an important disadvantage: you can't easily get to the username from within your service method. And in the UserNamePasswordValidator one has no access to OperationContext.Current. There are complex workarounds for this (see e.g. here), but still then you'd end up accessing your database once for authentication, and once to do the real work, which is inefficient. And it's not like huge amounts of code are needed to do your own authentication. It is somewhat less "elegant", but this really becomes relevant mostly when needing to refactor because of a change of the method of authentication to something else than username.

The end goal is to identify the user, i.e. get the user row id from the database during authentication, and then use the user row id to relate e.g. a database modification to the user using a foreign key. Typically in a business app you want to trace who has done what and when (audit), so you don't just want to authenticate the user and let him execute a service method, but you want to use his user id, and perhaps more (authorization) to perform an action.



WCF, .NET Remoting style (using WsHttpBinding)

clock September 25, 2009 06:40 by author Administrator

This last week I've been exploring WCF, and my aim was building a prototype client-server application, .NET remoting style. The requirements that I had for this applications were:

  • Serialize object graphs like you could do with .NET remoting (this application doesn't need platform independence).
  • Share data types between client and server, because they will be using the same platform. Also share the service interface itself.
  • Use http transport.
  • Use user name and password authentication.

The information in this post has been gathered from a bunch of other articles/blog posts, which I'll try to cite, if I didn't forget to write them down as I ran across it. The default scenario that microsoft wants developers to use is SOA, not sharing types between client/server, but generating proxy classes on the client side (which are not very useful in my experience, except in the simplest of applications). So deviating from this preferred path leads to quite a bit of googling to make WCF behave to satisfied aforementioned requirements. Also I have to mention Ingo Rammer's From .NET Remoting to Windows Communication Foundation (WCF) article. It comes quite close to meeting the requirements above, but it still uses some client project class generation (only for the service interface), and it doesn't demonstrate authentication. Time to get started! The source code of the entire project can be downloaded from here.

Creating the shared data assembly

Ok, let's start at the bottom, the data classes that will be shared by client and server. Because they will be shared they need to be in a separate assembly: BusinessApp.Data.dll. Below is the source code for the Product and Category class.

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace BusinessApp.Data {
    public class Product {
        public string Name { get; set; }

        public string Description { get; set; }

        public decimal Price { get; set; }
    }
}

Note that use of Contract attributes (DataContract/DataMember) is not required since .NET 3.5 SP1. The Category class holds a Product collection and a CheapestProduct property.

using System;
using System.Collections.Generic;
using System.Text;

namespace BusinessApp.Data {
    public class Category {
        private List<Product> products = new List<Product>();

        public string Name { get; set; }

        public List<Product> Products {
            get {
                return products;
            }
        }

        public Product CheapestProduct { get; set; }
    }
}

The instance referenced by CheapestProduct is also referenced by the Products collection. So that means we need to be able to serialize object graphs. WCF's default behavior is to just serialize them separately, so you'd end up with two copies of the originally single Product instance. Next I'm defining a (nonsensical) interface for querying products and categories:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace BusinessApp.Data {
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IService {
[OperationContract]
List<Product> GetAllProducts();

[OperationContract]
[CustomDataContractSerializer(true)]
List<Category> GetAllCategories();
}
}

The service interface is annotated with a ServiceContract attribute, and all operations are annotated with an OperationContract attribute. Method GetAllCategories is a bit special, because this method needs object graph serialization. Unfortunately there's no way you can configure this behavior in an app.config file. MS apparently wanted to discourage this practice by making the DataContractSerializer.PreserveObjectReferences property readonly (thus non-configurable). But, there is still a way to tell WCF to serialize object graphs in a convoluted manner, described here. This article shows how you create two classes CustomDataContractSerializerAttribute and CustomDataContractSerializerOperationBehavior to modify the DataContractSerializer behavior. The usage is pretty simple: just annotate the service interface method with [CustomDataContractSerializer(true)] and it will serialize object graphs properly (see method GetAllCategories above).

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;

namespace BusinessApp.Data {
/// <summary>
/// From
/// <see cref="http://blog.hill-it.be/post/2007/08/22/MaxItemsInObjectGraph-and-keeping-references-when-serializing-in-WCF"/>.
/// </summary>
public class CustomDataContractSerializerAttribute : Attribute, IOperationBehavior {
private bool _preserveReferences = false;
private int _maxItemsInObjectGraph = 65536;

public CustomDataContractSerializerAttribute(bool preserveReferences) {
_preserveReferences = preserveReferences;
}

public CustomDataContractSerializerAttribute(
bool preserveReferences,
int maxItemsInObjectGraph
) {
_preserveReferences = preserveReferences;
_maxItemsInObjectGraph = maxItemsInObjectGraph;
}

public void AddBindingParameters(
OperationDescription description,
BindingParameterCollection parameters
) {
}

public void ApplyClientBehavior(
OperationDescription description,
ClientOperation proxy
) {
IOperationBehavior innerBehavior =
new CustomDataContractSerializerOperationBehavior(
description,
_preserveReferences,
_maxItemsInObjectGraph
);
innerBehavior.ApplyClientBehavior(description, proxy);
}

public void ApplyDispatchBehavior(
OperationDescription description,
DispatchOperation dispatch
) {
IOperationBehavior innerBehavior =
new CustomDataContractSerializerOperationBehavior(
description,
_preserveReferences,
_maxItemsInObjectGraph
);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}

public void Validate(OperationDescription description) {
}
}
}
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;

namespace BusinessApp.Data {
/// <summary>
/// From
/// <see cref="http://blog.hill-it.be/post/2007/08/22/MaxItemsInObjectGraph-and-keeping-references-when-serializing-in-WCF"/>.
/// </summary>
public class CustomDataContractSerializerOperationBehavior :
DataContractSerializerOperationBehavior
{
private bool _preserveReferences = false;
private int _maxItemsInObjectGraph = 65536;

public CustomDataContractSerializerOperationBehavior(
OperationDescription operationDescription,
bool preserveReferences,
int maxItemsInObjectGraph
) : base(operationDescription)
{
_preserveReferences = preserveReferences;
_maxItemsInObjectGraph = maxItemsInObjectGraph;
}

public override XmlObjectSerializer CreateSerializer(
Type type,
string name,
string ns,
IList<Type> knownTypes
) {
return new DataContractSerializer(
type,
name,
ns,
knownTypes,
this.MaxItemsInObjectGraph,
this.IgnoreExtensionDataObject,
_preserveReferences,
this.DataContractSurrogate
);
}

public override XmlObjectSerializer CreateSerializer(
Type type,
XmlDictionaryString name,
XmlDictionaryString ns,
IList<Type> knownTypes
) {
return new DataContractSerializer(
type,
name,
ns,
knownTypes,
_maxItemsInObjectGraph,
this.IgnoreExtensionDataObject,
_preserveReferences,
this.DataContractSurrogate
);
}
}
}

Creating the Server

The service is hosted in a server process, in this prototype a simple console application is used as a server. First the IService interface is implemented.

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

using BusinessApp.Data;

namespace BusinessApp.WcfService {
public class Service : IService {
public List<Product> GetAllProducts() {
Console.WriteLine("GetAllProducts()");
List<Product> products = new List<Product>();
products.Add(new Product { Name = "Apple", Description = "Juicy apple", Price = 1.20m });
products.Add(new Product { Name = "Tomato", Description = "Red tomato", Price = 1.35m });
return products;
}

public List<Category> GetAllCategories() {
Console.WriteLine("GetAllCategories()");
List<Category> categories = new List<Category>();
Category foodCategory = new Category { Name = "Food" };
foodCategory.Products.AddRange(GetAllProducts());
foodCategory.CheapestProduct = foodCategory.Products[0];
categories.Add(foodCategory);
return categories;
}
}
}

Below is the server's main program:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;

using BusinessApp.Data;
using BusinessApp.WcfService;

namespace Server {
class Program {
static void Main(string[] args) {
try {
ServiceHost host = new ServiceHost(typeof(Service));
host.Open();
Console.WriteLine("Press a key to exit.");
Console.ReadKey();
host.Close();
} catch (Exception e) {
Console.WriteLine("Exception caught: " + e.ToString());
Console.WriteLine("Press a key to exit.");
Console.ReadKey();
}
}
}
}

The service is configured using an app.config file, the WsHttpBinding is used for end-to-end security (ws-security):


<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="BusinessApp.WcfService.ServiceBehavior" name="BusinessApp.WcfService.Service">
<!-- Must add base address for host -->
<host>
<baseAddresses>
<add baseAddress="http://localhost:8081/Service"/>
</baseAddresses>
</host>
<endpoint address="http://localhost:8081/Service"
binding="wsHttpBinding"
contract="BusinessApp.Data.IService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BusinessApp.WcfService.ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

Creating the Client

In this prototype a simple Win Forms application is used as client. Visual Studio 2008 allows adding a 'Service Reference' to your project, and this normally creates proxy classes in your project that can communicate with your service. When the project knows the data types used by the service (i.e. has a reference to the assembly containing the data types), then it optionally will not generate proxy classes for the data types. It however will still generate a mirror for the service interface IService itself. Though tempting as 'Add Service Reference' is, the service can be used using interface IService just as easily without resorting to the evil 'Add Service Reference' as we'll see below (reference: Using WCF with Automatic Client Proxies). Most of the effort goes into creating the client configuration file (app.config):


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8081/Service"
binding="wsHttpBinding"
contract="BusinessApp.Data.IService"
name="WsHttpService" >
</endpoint>
</client>
</system.serviceModel>
</configuration>

The client code of the main form:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Windows.Forms;

using BusinessApp.Data;

namespace BusinessApp.WindowsFormsApplication {
public partial class MainForm : Form {
public MainForm() {
InitializeComponent();
}

private void getAllProductsToolStripMenuItem_Click(object sender, EventArgs e) {
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("WsHttpService");
channelFactory.Credentials.UserName.UserName = "john";
channelFactory.Credentials.UserName.Password = "P@ssword";

IService service = channelFactory.CreateChannel();
try {
List<Product> products = service.GetAllProducts();
if (products == null) {
textBox.Text += "null" + Environment.NewLine;
} else {
foreach (Product product in products) {
textBox.Text += "Product: " + product.Name + Environment.NewLine;
}
}
textBox.Select(textBox.Text.Length, 0);
textBox.ScrollToCaret();
} finally {
// Not needed, because the security context is disabled at the server's binding configuration.
//((IChannel)service).Close();
}
}

private void getAllCategoriesToolStripMenuItem_Click(object sender, EventArgs e) {
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("WsHttpService");
channelFactory.Credentials.UserName.UserName = "john";
channelFactory.Credentials.UserName.Password = "P@ssword";

IService service = channelFactory.CreateChannel();
try {
List<Category> categories = service.GetAllCategories();
if (categories == null) {
textBox.Text += "null" + Environment.NewLine;
} else {
foreach (Category category in categories) {
textBox.Text += "Category: " + category.Name + Environment.NewLine;
textBox.Text += "Cheapest product: " + (category.CheapestProduct == null ? "null" : category.CheapestProduct.Name) + Environment.NewLine;
foreach (Product product in category.Products) {
textBox.Text += "Product: " + product.Name + Environment.NewLine;
}

// Let'
s see if we got an object graph, if so the
// product instance is also part of the category.Products collection
// and also changed.
if (category.CheapestProduct != null) {
category.CheapestProduct.Name = "XYZ";
textBox.Text += "--- After change ---" + Environment.NewLine;
textBox.Text += "Category: " + category.Name + Environment.NewLine;
textBox.Text += "Cheapest product: " + (category.CheapestProduct == null ? "null" : category.CheapestProduct.Name) + Environment.NewLine;
foreach (Product product in category.Products) {
textBox.Text += "Product: " + product.Name + Environment.NewLine;
}
}
}
}
textBox.Select(textBox.Text.Length, 0);
textBox.ScrollToCaret();
} finally {
// Not needed, because the security context is disabled at the server's binding configuration.
//((IChannel)service).Close();
}
}
}
}

As one can see, making a service call is very easy: create a ChannelFactory, and then create a channel, which returns a proxy that implements the IService interface. Now we can actually demo something! Whooo! Here's a screenshot of how the client should look like after making the call and logging the result:

WCF client demo screenshot

Figure 1: WCF client demo screenshot

Adding Authentication

To make it more useful in a real world business application, authentication has to be added. This prototype demonstrates use of Message authentication and use of an X509 certificate to encrypt the message. WCF doesn't allow username and password to be transported without any encryption. First let's make changes to the service configuration:


<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="MySecureBinding">
<security mode="Message">
<message clientCredentialType ="UserName" establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="BusinessApp.WcfService.ServiceBehavior" name="BusinessApp.WcfService.Service">
<!-- Must add base address for host -->
<host>
<baseAddresses>
<add baseAddress="http://localhost:8081/Service"/>
</baseAddresses>
</host>
<endpoint address="http://localhost:8081/Service"
binding="wsHttpBinding"
bindingConfiguration="MySecureBinding"
contract="BusinessApp.Data.IService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BusinessApp.WcfService.ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<!-- Create using this command: -->
<!-- makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange -pe -->
<serviceCertificate
findValue="MyServerCert"
x509FindType="FindBySubjectName"
storeLocation="LocalMachine"
storeName="My" />
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="BusinessApp.WcfService.CustomUserNameValidator, BusinessApp.WcfService"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

The bindings element tells WCF to secure messages, and use 'UserName' as the credential type. The endpoint refers to this binding by this attribute: bindingConfiguration="MySecureBinding".

Note that I've sneakily added the very very import attribute establishSecurityContext="false" to the binding's security's message configuration. If this is not done, then the channel MUST be closed by the client after it's not needed anymore. If it is not closed, then it would result in a dangling session at the server. The default session limit at the server is 10, so the server would no longer accept new calls after just 10 non properly closed sessions, this can happen in a split second! The wsHttpBinding by default has transport-level sessions if it has security (default) or reliable messaging. The downside to disabling the security context is that messages become larger, as every message must be individually secured (thanks to Nicholas Allen for clarifying this).

Further the service behavior element has some new child elements. The userNameAuthentication pointsto a custom .NET class that will authenticate the user through his user name/password. The serviceCertificate points to the certificate used for message encryption. Details were stolen from here. Create the test certificate using this command:

makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe

The validator that is referred to by the configuration file is as follows (it only allows one user):

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Web;

namespace BusinessApp.WcfService {
public class CustomUserNameValidator : UserNamePasswordValidator {
// This method validates users. It allows only user john with password P@ssword.
// This code is for illustration purposes only and
// MUST NOT be used in a production environment because it is NOT secure.
public override void Validate(string userName, string password) {
if (null == userName || null == password) {
throw new ArgumentNullException();
}

if (!(userName == "john" && password == "P@ssword")) {
throw new SecurityTokenException("Unknown Username or Password");
}
}
}
}

The client configuration file needs to be changed too:


<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8081/Service"
behaviorConfiguration="myClientBehavior"
binding="wsHttpBinding"
bindingConfiguration="MySecureBinding"
contract="BusinessApp.Data.IService"
name="WsHttpService" >
<identity>
<dns value="MyServerCert"/>
</identity>
</endpoint>

</client>
<bindings>
<wsHttpBinding>
<binding name="MySecureBinding">
<security mode="Message">
<message clientCredentialType ="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="myClientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication
certificateValidationMode="Custom"
customCertificateValidatorType="BusinessApp.WindowsFormsApplication.CustomX509CertificateValidator,BusinessApp.WindowsFormsApplication" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

New in the configuration is the wsHttpBinding specifying the clientCredentialType to be UserName. By default WCF doesn't like to use self signed certificates, so a small hack is needed to make WCF a bit more forgiving. This is done by creating a CustomX509CertificateValidator class:

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Web;

namespace BusinessApp.WindowsFormsApplication {
/// <summary>
/// Need this class to accept self signed certificates.
/// See <see cref="http://www.devatwork.nl/index.php/2007/05/31/wcf-username-authentication/"/>.
/// </summary>
public class CustomX509CertificateValidator : X509CertificateValidator {
public override void Validate(X509Certificate2 certificate) {
// validate argument
if (certificate == null) {
throw new ArgumentNullException("certificate");
}

// check if the name of the certifcate matches
if (certificate.SubjectName.Name != "CN=MyServerCert") {
throw new SecurityTokenValidationException("Certificated was not issued by trusted issuer");
}
}
}
}

Now all that's left to be done is specify the username/password in the client code as the service call is made:

ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("WsHttpService");
channelFactory.Credentials.UserName.UserName = "john";
channelFactory.Credentials.UserName.Password = "P@ssword";

IService service = channelFactory.CreateChannel();
List<Product> products = service.GetAllProducts();

That's it, mission completed!

Sniffing network traffic

I used to use the very nice tcptrace tool to put in between client and server and look at the http messages when anything went wrong. This no longer seemed to work when working with WCF, because WCF would check the service address, and it detected a mismatch in the address (port). So by default WCF is a bit more critical than .NET remoting used to be. But luckily there's a solution to this! Just change the AddressFilterMode to Any using the ServiceBehaviorAttribute. Add this attribute to your Service implementation class:

 [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class Service : IService {


Version history

Nov 12, 2009: set establishSecurityContext to false to disable sessions for the wsHttpBinding.



About the author

Wout is trying to keep up with the latest .NET developments with minimal success!

Page List

    Sign in