Welcome to MSDN Blogs Sign in | Join | Help

WCF Silverlight: Exception and Serialization

Exception is used as a mechanism to report issue by an application.  In WCF webservice implementation, an application may throw exception which will be wrapped with Fault and sends over to the client.  However, if an application chooses to return Exception as parameter instead, the exception needs to be serialized.  I like to describe how the exception serialization works, how the import tool (svcutil/slsvcutil) generates codes for those classes and what is the different between .Net Framework/Silverlight implementation.

WCF’s DataContractSerializer serialize s Exception class via ISerializable (Exception in .Net Framework implements ISerializable).  However, in Silverlight, there is no ISerializable  interface.  Therefore, Serialization information of Exception transferred over the wire will not be set into the Exception class.  This does not only apply to Exception but also any types implementing ISerializable on the .Net Framework serialized to Silverlight.  Siverlight proxy generation works around the issue by generating a type called ArrayOfXElement instead to capture all information as XElements (code snippet below).

[System.Xml.Serialization.XmlSchemaProviderAttribute(null, IsAny = true)]

[System.CodeDom.Compiler.GeneratedCodeAttribute("slsvcutil", "2.0.5.0")]

public class ArrayOfXElement : System.Xml.Serialization.IXmlSerializable

{

 

    private System.Collections.Generic.List<System.Xml.Linq.XElement> nodes = new System.Collections.Generic.List<System.Xml.Linq.XElement>();

 

    public ArrayOfXElement()

    {

    }

 

    public virtual System.Collections.Generic.List<System.Xml.Linq.XElement> Nodes

    {

        get

        {

            return this.nodes;

        }

    }

 

    public virtual System.Xml.Schema.XmlSchema GetSchema()

    {

        throw new System.NotImplementedException();

    }

 

    public virtual void WriteXml(System.Xml.XmlWriter writer)

    {

        System.Collections.Generic.IEnumerator<System.Xml.Linq.XElement> e = nodes.GetEnumerator();

        for (

        ; e.MoveNext();

        )

        {

            ((System.Xml.Serialization.IXmlSerializable)(e.Current)).WriteXml(writer);

        }

    }

 

    public virtual void ReadXml(System.Xml.XmlReader reader)

    {

        for (

        ; (reader.NodeType != System.Xml.XmlNodeType.EndElement);

        )

        {

            if ((reader.NodeType == System.Xml.XmlNodeType.Element))

            {

                System.Xml.Linq.XElement elem = new System.Xml.Linq.XElement("default");

                ((System.Xml.Serialization.IXmlSerializable)(elem)).ReadXml(reader);

                Nodes.Add(elem);

            }

            else

            {

                reader.Skip();

            }

        }

    }

}

 

Let’s discuss this in two scenarios

1)      Webservice call returning Exceptions defined in mscorlib.dll and System.ServiceModel.dll assemblies.

2)      Webservice call returning other Exceptions (eg. MyCustomException).

1)   Webservice call returning Exceptions defined in mscorlib.dll and System.ServiceModel.dll assemblies.

[ServiceContract]

public interface IEchoService

{

    [OperationContract]

    Exception GetException();

 

    [OperationContract]

    ArgumentException GetArgumentException();

 

    [OperationContract]

    FaultException GetFaultException();

}

By default, the webservice importing tools such as svcutil/slsvcutil (or VS’s Add Service Reference…) add references to mscorlib.dll and System.ServiceModel.dll assembly.  Hence, the tool will not generate any codes for those types.   You may, however, pass the flag /nostdlib to not include those assemblies as references and the type will be generated.

Below shows codes generated for .Net Framework for Exception class.

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

[System.SerializableAttribute()]

public partial class Exception : object, System.Runtime.Serialization.ISerializable

{

 

    private System.Runtime.Serialization.SerializationInfo info;

 

    public Exception(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)

    {

        this.info = info;

    }

 

    public System.Runtime.Serialization.SerializationInfo SerializationInfo

    {

        get

        {

            return this.info;

        }

        set

        {

            this.info = value;

        }

    }

 

    public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)

    {

        if ((this.SerializationInfo == null))

        {

            return;

        }

        System.Runtime.Serialization.SerializationInfoEnumerator enumerator = this.SerializationInfo.GetEnumerator();

        for (

        ; enumerator.MoveNext();

        )

        {

            System.Runtime.Serialization.SerializationEntry entry = enumerator.Current;

            info.AddValue(entry.Name, entry.Value);

        }

    }

}

Code generated for Silverlight will simply be ArrayOfXElement as shown earlier.

Note: One limitation of ISerializable generated type is it relies on the code namespace as its namespace uri.  If you try to change the namespace such as using /namespace flag, the import will fail with the below error.

Error: Cannot import wsdl:portType

Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter

Error: ISerializable type with data contract name 'Exception' in namespace 'http://schemas.datacontract.org/2004/07/System' cannot be imported. The data contract namespace cannot be customized for ISerializable types and the generated namespace 'wcf' does not match the required CLR namespace 'System'. Check if the required namespace has been mapped to a different data contract namespace and consider mapping it explicitly using the namespaces collection.

XPath to Error Source: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:portType[@name='IService']

 

2)  Webservice call returning other Exceptions (eg. MyCustomException).

An application may define a custom Exception or use an Exception defined in other assembly besides mscorlib and System.ServiceModel.

[ServiceContract]

public interface IEchoService

{

    [OperationContract]

    MyCustomException GetCustomException();

}

In this case, that exception class will be generated. 

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

[System.SerializableAttribute()]

public partial class MyCustomException : System.Exception

{

 

    public MyCustomException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) :

        base(info, context)

    {

    }

}

Code generated for Silverlight will simply strip out SerializableAttribute and the ctor with SerializationInfo (not exist in Silverlight).

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

public partial class MyCustomException : System.Exception

{

}

Notice that the class is generated deriving from System.Exception since mscorlib is referenced by default.  If you specify /nostdlib explicitly, the System.Exception class will also be generated and, in Silverlight, all ISerializable types will be replaced with ArrayOfXElement accordingly.  

Note: you may explicitly reference (/reference) to an assembly defining MyCustomException class to avoid the code generation altogether.

Posted by suwatch | 1 Comments

WCF and Fault

Fault is generally used as a way for webservices to report rich error information to clients who perform webservice call (think .Net exception).  WCF has a rich support for webservice to provide fault information.

1)      Throw typical exception

2)      Throw FaultException

3)      Throw FaultException with strongly-typed detail

Note: In either case, this is considered application fault and the client channel state will not be affected and continue to function.   There, however, are infrastructure faults such as security that could fault the channel.

1)      Service Implementation throws typical exception (not of FaultException types).  Such as Exception, ArgumentNullException etc.

[ServiceContract]

public interface IEchoService

{

    [OperationContract]

    string Echo(string msg);

}

 

public class EchoService : IEchoService

{

    public string Echo(string msg)

    {

        throw new Exception("Echo fault!");

    }

}

WCF service will simply wrap the exception with a Fault message and return to a client.   The client will receive a generic fault (FaultException thrown) containing a generic message.

System.ServiceModel.FaultException: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

If webservice would like to include the exception information into the fault, it may turn on the IncludeExceptionDetailInFaults setting (off by default).

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]

public class EchoService : IEchoService

{

    public string Echo(string msg)

    {

        throw new Exception("Echo fault!");

    }

}

WCF service will wrap the exception with a Fault message including all the details and return to a client.   The client will receive a fault (FaultException<ExceptionDetail> thrown) containing a detail message.

System.ServiceModel.FaultException< System.ServiceModel.ExceptionDetail>: Echo fault!

 

2)      Service Implementation throws Exception of FaultException type.

WCF provides FaultException classes allowing application to define faultcode, reason, etc.  The application may construct and throw the FaultException explicitly as follow.

[ServiceContract]

public interface IEchoService

{

    [OperationContract]

    string Echo(string msg);

}

 

public class EchoService : IEchoService

{

    public string Echo(string msg)

    {

        throw new FaultException("Echo fault!");

    }

}

WCF service will simply serialize the FaultException as a Fault message and return to a client.  All data in the FaultException will be send to client regardless of  IncludeExceptionDetailInFaults setting.   The client will receive a fault (FaultException thrown) with data defined by the services.

System.ServiceModel.FaultException: Echo fault!

 

3)      Service Implementation throws FaultException type  with strongly-typed detail

In this case, WCF provides a FaultException<T> class.  This allows application to define a strong typed detail with the FaultException.  The client would be able to catch FaultException<T> explicitly.  The application must decorate the operation with  [FaultContract] to assist serialization process.

[DataContract]

public class MyCustomDetail

{

    [DataMember]

    public string ID;

 

    public MyCustomDetail(string id) { ID = id; }

}

 

[ServiceContract]

public interface IEchoService

{

    [OperationContract]

    [FaultContract(typeof(MyCustomDetail))]

    string Echo(string msg);

}

 

public class EchoService : IEchoService

{

    public string Echo(string msg)

    {

        throw new FaultException<MyCustomDetail>(new MyCustomDetail(1));

    }

}

The client will receive a fault (FaultException thrown) with strongly-typed detail.

System.ServiceModel.FaultException< MyCustomDetail>: The detail contains the strong-typed detail (MyCustomDetail)

 

Posted by suwatch | 1 Comments

WCF Duplex Channel State and Exception

While testing the Duplex Channel with Callback implementation, I found out how the fault or various exceptions on the callback would affect the Duplex channel state. 

1)      Callback implementation received a message with an invalid or an unexpected action.    In this case, Duplex Channel will be closed and can no longer be used for communication.

 

2)      Callback implementation received a message with an invalid or an unexpected soap body.  Duplex Channel will try to return the fault back to the caller.  The Duplex Channel state is not affected by this and continues to function.

 

3)      Callback implementation explicitly throws an Exception of FaultException type. 

class EchoCallback : IEchoCallback

{

    public void OnEcho(string msg)

    {

        throw new FaultException("fault reason");

    }

}

 

Duplex Channel will try to return the fault back to the caller.  The Duplex Channel state is not affected by this and continues to function.

 

4)      Callback implementation explicitly throws an Exception which is not of FaultException type. 

class EchoCallback : IEchoCallback

{

    public void OnEcho(string msg)

    {

        throw new Exception("exception message");

    }

}

 

By default, the exception is considered unhandled and the Duplex Channel state will aborted and can no longer be used for communication.

Posted by suwatch | 1 Comments

RemotingException: “All pipe instances are busy.” or “The system cannot find the file specified.”

Today, I ran into an interesting issue.  One of our customers was using the .Net Remoting IPC client in the asp.net application communicating with a local service.  The asp.net application was configured with impersonation.  The IPC client was simply making multiple calls to the remoting service.  Every now and then, the call failed RemotingException with “All pipe instances are busy.” (error=231) and “The system cannot find the file specified.” (error=2).  Upon investigation, the issue seemed to be a race condition issue with creating and closing the Win32 namepiped handle.  Typically, IPC .Net Remoting client will cache and reuse the namedpipe handles.  Since the namedpipe creation is security context sensitive, if the calling thread is impersonated, the namedpipe will not be shared.   Calling remoting service in a tight loop will cause new namedpipe handle to be created and closed.  The issue is repro-able by just a native Win32 namedpipe sample posted on msdn (http://msdn.microsoft.com/en-us/library/aa365588(VS.85).aspx and http://msdn.microsoft.com/en-us/library/aa365592(VS.85).aspx).  I simply put the client into a while loop.  After just a few seconds on my Vista and Windows 2008 machine, the error will occur.

Good news is the workaround is simple.  One should call WaitNamedPipe and retry creating the namedpipe.  One catch is that WaitNamedPipe is also subject to this issue and, sometimes, return immediately with and “The system cannot find the file specified.” (error=2).  You may want to retry the WaitNamedPipe a couple of time before calling recreate the namedpipe handle.  I have a code snippet below that I modified from the sample mentioned above.  With this, the client will run without any issue.  You could adapt this in the Remoting IPC client scenario by Pinvoke WaitNamedPipe in the same manner before retrying invoking the remoting service.

 

//

// PipeClient.cpp : Defines the entry point for the console application.

//

 

#include <windows.h>

#include <stdio.h>

#include <conio.h>

#include <tchar.h>

#include "stdafx.h"

 

#define BUFSIZE 512

 

int _tmain(int argc, TCHAR *argv[])

{

   HANDLE hPipe;

   LPTSTR lpvMessage=TEXT("Default message from client");

   TCHAR chBuf[BUFSIZE];

   BOOL fSuccess;

   DWORD cbRead, cbWritten, dwMode;

   LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

 

   if( argc > 1 )

      lpvMessage = argv[1];

 

 

   while (1)

   {

// Try to open a named pipe; wait for it, if necessary.

         BOOL bRetried = FALSE;

         while (1)

         {

              hPipe = CreateFile(

                   lpszPipename,   // pipe name

                   GENERIC_READ |  // read and write access

                   GENERIC_WRITE,

                   0,              // no sharing

                   NULL,           // default security attributes

                   OPEN_EXISTING,  // opens existing pipe

                   0,              // default attributes

                   NULL);          // no template file

       

         // Break if the pipe handle is valid.

       

              if (hPipe != INVALID_HANDLE_VALUE)

                   break;

       

              // Exit if an error other than ERROR_PIPE_BUSY occurs.

       

              if (GetLastError() != ERROR_PIPE_BUSY)

              {

                   printf("Could not open pipe");

                   return 0;

              }

 

              if (bRetried)

              {

                   printf("Could not open pipe");

                   return 0;

              }

 

              // Retry for 1000ms

              bRetried = TRUE;

              BOOL bSuccess = FALSE;

              for (INT i = 0 ; i < 10 ; ++i)

              {

                   _tprintf( TEXT("W") );

                   bSuccess = WaitNamedPipe(lpszPipename, 0);

                   if (!bSuccess)

                   {

                         Sleep(100);

                   }

                   else

                         break;

              }

 

              if (!bSuccess)

              {

                   printf("Fail WaitNamedPipe");

                   return 0;

              }

              else

                  _tprintf( TEXT("R") );

         }

       

      // The pipe connected; change to message-read mode.

       

         dwMode = PIPE_READMODE_MESSAGE;

         fSuccess = SetNamedPipeHandleState(

              hPipe,    // pipe handle

              &dwMode,  // new pipe mode

              NULL,     // don't set maximum bytes

              NULL);    // don't set maximum time

         if (!fSuccess)

         {

              printf("SetNamedPipeHandleState failed");

              return 0;

         }

       

      // Send a message to the pipe server.

       

         fSuccess = WriteFile(

              hPipe,                  // pipe handle

              lpvMessage,             // message

              (lstrlen(lpvMessage)+1)*sizeof(TCHAR), // message length

              &cbWritten,             // bytes written

              NULL);                  // not overlapped

         if (!fSuccess)

         {

              printf("WriteFile failed");

              return 0;

         }

       

         do

         {

         // Read from the pipe.

       

              fSuccess = ReadFile(

                   hPipe,    // pipe handle

                   chBuf,    // buffer to receive reply

                   BUFSIZE*sizeof(TCHAR),  // size of buffer

                   &cbRead,  // number of bytes read

                   NULL);    // not overlapped

       

              if (! fSuccess && GetLastError() != ERROR_MORE_DATA)

                   break;

       

              //_tprintf( TEXT("%s\n"), chBuf );

              _tprintf( TEXT(".") );

         } while (!fSuccess);  // repeat loop if ERROR_MORE_DATA

 

         //getch();

       

         CloseHandle(hPipe);

   }

 

   return 0;

}

 

Posted by suwatch | 0 Comments

Tutorial: Using Silverlight web service client configuration

Overview

Microsoft Silverlight provides a rich platform to write internet applications.  One of its key features is to enable the applications to communicate with the web services.  Microsoft Silverlight Beta1 version was equipped with the web service client stack.  The web service team has recently added web client configuration support (available in Beta2 and later release).  This feature enables deployment time configuration such as changing targeted service endpoints or various behavior settings without having to rebuild and redeploy the binaries.  Below illustrates the step-by-step tutorial.

Prerequisites

-          Microsoft Silverlight 2.0 Beta 1.

-          Microsoft Visual Studio 2008.

-          Microsoft Silverlight Tools Beta 1 for Visual Studio 2008.

-          Microsoft Silverlight SDK 2.0 Beta 1.

Note: available from the Silverlight download site.

Getting started

1.       Before we get started, we have to create a web service.  You may follow this instruction on how to create and configure WCF web service to work with Silverlight.  Create 2 web services to experience the configuration effects on the Silverlight client and deploy the web services at http://localhost/webservice.   Use browser to navigate to http://localhost/webservice/service1.svc and http://localhost/webservice/service2.svc to ensure the services are up and running.

namespace CustomerService

{

    public class Service1 : IService1

    {

        public string GetData(int value)

        {

            return string.Format("Serivce1: You entered {0}", value);

        }

    }

 

    public class Service2 : IService1

    {

        public string GetData(int value)

        {

            return string.Format("Serivce2: You entered {0}", value);

        }

    }

}

 

2.       Use Visual Studio 2008 to create a Silverlight Application.  Add a <TextBlock/> in page.xaml.  This TexBlock will be used to display the result from web service calls.

Add textblock 

 

3.       Generate a proxy to a web service.  Select the SilverlightApplication1 on “Solution Explorer”, right click and select “Add Service Reference…” .  Enter http://localhost/webservice/service1.svc in address box.  Then click Go and OK.

Add Service References... 

 

4.       The proxy to the webservice is now created along with the client configuration (ServiceReferences.ClientConfig).  The file will be deployed along with other silverlight application components.   If you are familiar with .Net 3.0 configuration, this file has similar contents (a subset) and generally used to configure the client proxy.  

 

5.       Add codes to make a web service call and display the result using the textblock.

namespace SilverlightApplication1

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

            // hookup page's loaded event

            Loaded += new RoutedEventHandler(Page_Loaded);

        }

 

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            // call service

            ServiceReference1.Service1Client proxy = new

                ServiceReference1.Service1Client();

            proxy.GetDataCompleted += new

                EventHandler<ServiceReference1.GetDataCompletedEventArgs>

                (GetDataCompleted);

            proxy.GetDataAsync(1);

        }

 

        void GetDataCompleted(object sender,

            ServiceReference1.GetDataCompletedEventArgs e)

        {

            // display on textblock

            uxText.Text = e.Result;

        }

    }

}

 

6.       Compile and run the application.  You will see the result returned from Service1 as below.

  

 

7.       Let’s assume, at deployment time, you want to direct the client to a different web service (Service2.svc).  You could simply edit a ServiceReferences.ClientConfig and modify the endpoint address (see below).    

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicHttpBinding_IService1"

            maxBufferSize="65536"

            maxReceivedMessageSize="65536">

          <security mode="None" />

        </binding>

      </basicHttpBinding>

    </bindings>

    <client>

      <endpoint address="http://localhost/webservice/Service2.svc"

          binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding_IService1"

          contract="SilverlightApplication1.ServiceReference1.IService1"

          name="BasicHttpBinding_IService1" />

    </client>

  </system.serviceModel>

</configuration>

 

8.       Once you repackage this file into the XAP and run the application (or by simply recompile the application).  You will see the result returned from Service2.svc.

 

 

Conclusion

The webservice client configuration is very useful for deployment time configuration.  For Silverlight application, it contains a subset of .Net WCF client configuration.  Unlike .Net configuration, the configuration file must be named “ServiceReferences.ClientConfig”, packaged and deployed along with the application.    

 

Posted by suwatch | 2 Comments

X509 and WCF Security

WCF security provides confidentiality and integrity for message exchanges.  We support both symmetric and asymmetric cryptography implementation.  In this blog, I like to discuss WCF security using X509 (asymmetric) specifically.  By the end of this blog, you should be able to answer the following questions.

 

1) How do I assign the client or service X509 for WCF to use?

2) How do I define the validation or authentication for the incoming X509 and what is the default?

3) How do WCF enable protocol transition (map to windows account)?

 

Prerequisite

I assume you have had experiences with WCF writing its services or clients.  It is beneficial if you are familiar with X509 and PKI (Public Key Infrastructure) how they are used to secure communications.

 

General

It turns out the answers for the above questions depend on the bindings and configurations being used.  Hence, before jumping into the answer, I would like to discuss briefly what WCF bindings utilizing X509 are available.

 

WCF enables three different bindings using X509 namely Https, SSL over Tcp and Message Security.

 

- Https is one of the most common protocols adopted by internet security.  Generally, it is intended for server authentication using TLS/SSL protocol to exchange server’s X509.  WCF enables this thru its standard http bindings with Transport as well as TransportWithMessageCredential as a security mode and, for custom binding, HttpsTranportBindingElement.  In addition, the protocol allows client to send its X509 to identify itself to the server.  If Transport security mode is used, the client’s x509 is transmitted via schannel (sspi).   If TransportWithMessageCredential security mode is used, the client’s x509 is transmitted in the soap message (http payload). 

 

Note: schannel is one of the security service providers (ssp) shipped with the operating system that provides SSL/TLS implementation.

 

- SSL over Tcp uses SSL protocol over Tcp.   WCF enables this thru its standard NetTcpBinding with Transport and TransportWithMessageCredential security modes and, for custom binding, SslStreamSecurityBindingElement.  Similar to Https above, client could send its X509 to identify itself to the server.  If Transport security mode is used, the client’s x509 is transmitted via schannel (sspi).   If TransportWithMessageCredential security mode is used, the client’s x509 is transmitted in the soap message.

 

- Message Security uses WS-* protocol to secure messages.  WCF enables this thru its various standard bindings with Message security mode and, for custom binding, SecurityBindingElement.CreateXXXBindingElement.  It supports both SSL and MutualCertificate protocol.  The difference is how client obtains the server’s X509. Client could send its X509 to identify itself to the server.

 

Now let’s try to answer the question.

 

How to assign the server X509

For Https, it is done using httpcfg tool (httpcfg set ssl).  If it is webhosted, one could also specify the server’s X509 using IIS manager UI (under properties->Directory Security).

For SSL over Tcp and Message Security, it is specified thru ServiceHost.Credentials.ServiceCertificate property.

 

How to assign the client X509

Client’s X509 is always specified thru ChannelFactory.Credentials.ClientCertificate property.  There are a few catches for Https.  First, the client’s X509 issuer must be in Trusted root (belong to IssuerListInfoEx); otherwise, it will never be transmitted to server (403 forbidden).  Second, when specifying server X509 using httpcfg tool, make sure “-f 2” passed as parameter indicating negotiate client X509.  For web-hosted, you will also have to turn on ‘require client certificate’ checkbox in iis manager (directory security tab).

 

How to provide authentication for incoming client’s X509

For Https, the server always validates incoming client’s X509 using chain trust (chained to trusted root). 

For SSL over Tcp and Message Security, it is specified at ServiceHost.Credentials.ClientCertificate.Authentication property; by default, the X509 must chain to trusted root.  This also applies when Https is used but client’s X509 is transmitted using soap message (i.e. TransportWithMessageCredentials mode).

 

How to provide authentication for incoming server’s X509

For Https, it is specified at System.Net.ServicePointManager.ServerCertificateValidationCallback; by default, the X509 must chain to trusted root and Dns name must match with server address.

For SSL over Tcp and Message Security, it is specified at channelFactory.Credentials.ServiceCertificate.Authentication property; by default, the X509 must chain to trusted root.

 

Map Client’s X509 to Windows Account

WCF provides a knob to enable the mapping (ServiceHost.Credentials.ClientCertificate.Authentication.MapClientCertificateToWindowsAccount); by default, false (no mapping).  WCF supports two ways for mapping X509 to Windows Account; namely schannel mapping and UPN mapping.

- Schannel mapping: this is only supported if TLS/SSL protocol is used.  In short, if X509 is associated with the account at AD (Active Directory), schannel will simply map to that account.  Otherwise, schannel enforces NT_AUTH policy on the certificate and use S4U over UPN SubjAltNames or some heuristic derivation of X509 issuer name.

- UPN mapping: if Scahnnel mapping is not available, WCF will enforce NT_AUTH on X509 and manually perform S4U logon over UPN SubjAltNames.   Note: in Vista, WCF uses the latest LsaLogon api that supports X509-Windows mapping.

 

Now let’s try to answer the question.

 

For Https, this varies between self-hosted and web-hosted scenario.  For self-hosted, although TLS/SSL was used, there was limitation in OM causing Schannel mapping to be unavailable.  Therefore, only UPN mapping is used.  This will likely be addressed in the future CLR version.  For web-hosted, IIS provides a client X509 mapping feature.  If the X509 is configured for mapping, the mapped Windows account will be used; otherwise, UPN mapping.   BTW, if Https is used but client’s X509 is transmitted thru soap message (i.e. TransportWithMessageCredentials mode), only UPN mapping is applied.

For SSL over Tcp, although SSL was used, there was limitation in OM causing Schannel mapping to be unavailable.  Therefore, only UPN mapping is used.  This will likely be addressed in the future CLR version.

For Message Security, if TLS/SSL binding is used, WCF will try Schannel mapping then UPN mapping.  For other binding (i.e. MutualCertificate), only UPN mapping is applied. 

 

Summarize

The tables below briefly summarize what we just discussed.

 

 

Server X509 provision

Client X509 provision

Https

For self host, use httpcfg tool.  For webhost, use IIS manager.

ChannelFactory.Credentials

SSL over Tcp

ServiceHost.Credentials

ChannelFactory.Credentials

Message Security

ServiceHost.Credentials

ChannelFactory.Credentials

 

 

Server X509 authentication

Client X509 authentication

Https

ServicePointManager.

ServerCertificateValidationCallback

ChainTrust

SSL over Tcp

ChannelFactory.Credentials

ServiceHost.Credentials

Message Security

ChannelFactory.Credentials

ServiceHost.Credentials

 

 

Client X509 Windows Mapping

Https

For selfhost, Upn Mapping.  For webhost, IIS mapping then Upn Mapping

SSL over Tcp

Upn Mapping

Message Security

If SSL is used, try schannel mapping then Upn Mapping.

 

Posted by suwatch | 4 Comments
Filed under:
 
Page view tracker