March 2006 - Posts

Last week I received some feedback from the web services guru Christian Weyer (By the way, a cool guy too) about my post "Proxy Factory", so I decided to develop a complete and usable sample to show that concept.

In that post, I described a way to separate the service interface from the service implementation doing something similar to what WCF does when it creates a channel for a specific interface.

During this post, I will give a brief description about the main parts of this sample.

First of all, I defined a service interface or contract for my service. I did not use my imagination at all and I decided to call this interface "IHelloWorld".

public interface IHelloWorld

{

  string HelloWorld(string message);

}

The client application uses that interface and the service factory to create a proxy for the service implementation without care about any details of this last one.

class Program

{

  static void Main(string[] args)

  {

     //Default implementation

     ServiceFactory<IHelloWorld> factory = new ServiceFactory<IHelloWorld>();

     IHelloWorld service = factory.Create();

     Console.WriteLine(service.HelloWorld("John Doe"));

 

     //Remote implementation

     factory = new ServiceFactory<IHelloWorld>("RemoteImplementation");

     service = factory.Create();

     Console.WriteLine(service.HelloWorld("John Doe"));

   }

}

The proxy for the service implementation perform the following steps

  1. Looks for the correct service implementation using the provided interface
  2. Creates an instance of the service implementation
  3. Invokes the right method on the service implementation

For this sample, the proxy is quite simple and invokes the real service in the current machine, but more complex tasks could be performed, such as message interception or remote invocation. (WCF performs both things, it uses message interception to create a soap message and remote invocation to execute the service implementation in other machine).

public class ServiceProxy : System.Runtime.Remoting.Proxies.RealProxy

{

  private Type type;

  private string configurationName;

 

  public ServiceProxy(string configurationName, Type type) : base(type)

  {

    this.type = type;

    this.configurationName = configurationName;

  }

 

  public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage imsg)

  {

    Type implementationType = null;

    string type = this.type.FullName;

    foreach (ServiceInterfaceDefinition definition in ServiceInterfaceConfiguration.Current.Interfaces)

    {

      if ((this.configurationName == null || this.configurationName == definition.Name) && type == definition.Type)

      {

        implementationType = definition.ImplementationType;

        break;

      }

    }

 

    if(implementationType == null)

      throw new Exception("The implementation type can not be null");

 

    object implementation = Activator.CreateInstance(implementationType);

    ReturnMessage retmsg = null;

    if (imsg is IMethodCallMessage)

    {

      IMethodCallMessage call = imsg as IMethodCallMessage;

      object returnValue = implementationType.InvokeMember(call.MethodName, System.Reflection.BindingFlags.InvokeMethod,

              null, implementation, call.Args);

      retmsg = new ReturnMessage(returnValue, null, 0, null, call);

    }

 

    return retmsg;

  }

}

The configuration for the client application contains two service implementations in this case, "LocalImplementation" for a component in the same application and "RemoteImplementation" for a web service proxy.

<serviceInterfaces>

  <interfaces>

    <add name="LocalImplementation"

        implementationType="ServiceInterfaces.Client.LocalHelloWorld, ServiceInterfaces.Client"

        type="ServiceInterfaces.IHelloWorld">

    </add>

    <add name="RemoteImplementation"

         implementationType="ServiceInterfaces.Client.RemoteHelloWorld, ServiceInterfaces.Client"

         type="ServiceInterfaces.IHelloWorld">

    </add>

  </interfaces>

</serviceInterfaces>

The code below shows the service implementation configured as "LocalImplementation".

class LocalHelloWorld : IHelloWorld

{

  #region IHelloWorld Members

  public string HelloWorld(string message)

  {

    return String.Format("Local Hello World : {0}", message);

  }

  #endregion

}

Download the complete sample from here

Posted by cibrax
Filed under: ,

I have developed a new WS-Compression implementation for WCF.  This code is based on the WSE 3.0 implementation  made by my partner Rodolfo Finochieti.

 

Some features provided by this implementation:

 

  • Message body compression
  • Different compression algorithms (GZIP, Deflate)
  • Different compression levels
  • Support to enable compression by means of configuration or code

 

You can download it from here

UPDATED: A new version for the WCF september RC is available here

Enjoy :)

Posted by cibrax | 10 comment(s)
Filed under:

WCF by default maintains a cache for security tokens per channel instance (A channel is related to a contract). Therefore, it is not possible to reuse the same token for different channel instances.

Consider the following sample, a client application that consumes different services using a SAML token.

 

IHelloWorldChannel helloWorldService = factory.CreateChannel();

string response = helloWorldService.HelloWorld("John Doe");

Console.WriteLine(response);

helloWorldService = factory.CreateChannel();

response = helloWorldService.HelloWorld("John Doe 2");

Console.WriteLine(response);

factory = new ChannelFactory<IAnotherChannel>("anotherService");

helloWorldService = factory.CreateChannel();

response = helloWorldService.HelloWorld("John Doe 3");

Console.WriteLine(response);

 

In this case, I used three different channel instances and therefore a different SAML token for each service. (Each channel made an addition call to the STS in order to ask for a SAML token).

Fortunately, WCF provides a way to cache tokens outside the scope of a channel and reuse them later until they expire. 

During the course of this post, I will show the required steps to build a client-side token cache to reuse tokens obtained from a STS.  

 

First of all, I created a custom ClientCredentials class in order to return a custom SecurityTokenManager class. The SecurityTokenManager class is a kind of entry point to modify the process involved in the creation of a security token.

 

/// <summary>

/// Custom implementation

/// </summary>

class CustomClientCredentials : ClientCredentials

{

  public CustomClientCredentials()

      : base()

  {

  }

  protected CustomClientCredentials(ClientCredentials other)

     : base(other)

  {

  }

  protected override ClientCredentials CloneCore()

  {

    return new CustomClientCredentials(this);

  }

  /// <summary>

  /// Returns a custom security token manager

  /// </summary>

  /// <returns></returns>

  public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager()

  {

    return new CustomClientCredentialsSecurityTokenManager(this);

  }

}

 

Secondly, I declared my own SecurityTokenManager.

 

class CustomClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager

{

  private static Dictionary<Uri, CustomIssuedSecurityTokenProvider> providers = new Dictionary<Uri, CustomIssuedSecurityTokenProvider>();

  public CustomClientCredentialsSecurityTokenManager(ClientCredentials credentials)

       : base(credentials)

  {

  }

  /// <summary>

  /// Returns a custom token provider when a issued token is required

  /// </summary>

  public override System.IdentityModel.Selectors.SecurityTokenProvider CreateSecurityTokenProvider(System.IdentityModel.Selectors.SecurityTokenRequirement tokenRequirement)

  {

    if (this.IsIssuedSecurityTokenRequirement(tokenRequirement))

    {

      IssuedSecurityTokenProvider baseProvider = (IssuedSecurityTokenProvider)base.CreateSecurityTokenProvider(tokenRequirement);

      CustomIssuedSecurityTokenProvider provider = new CustomIssuedSecurityTokenProvider(baseProvider);

      return provider;

    }

    else

    {

      return base.CreateSecurityTokenProvider(tokenRequirement);

    }

  }

}

 

For this sample, I only want to cache issued tokens (Tokens obtained from a STS) and thefore I am using the IsIssuedSecurityTokenRequeriment method to determine if the channel is requesting an issued token or not. 

 

Lastly, I created a simple Cache helper and a custom token provider to reuse the issued tokens.

 

/// <summary>

/// Helper class used as cache for security tokens

/// </summary>

class TokenCache

{

   private const int DefaultTimeout = 1000;

   private static Dictionary<Uri, SecurityToken> tokens = new Dictionary<Uri, SecurityToken>();

   private static ReaderWriterLock tokenLock = new ReaderWriterLock();

   private TokenCache()

   {

   }

   public static SecurityToken GetToken(Uri endpoint)

   {

     SecurityToken token = null;

     tokenLock.AcquireReaderLock(DefaultTimeout);

     try

     {

       tokens.TryGetValue(endpoint, out token);

       return token;

     }

     finally

     {

       tokenLock.ReleaseReaderLock();

     }

   }

   public static void AddToken(Uri endpoint, SecurityToken token)

   {

     tokenLock.AcquireWriterLock(DefaultTimeout);

     try

     {

       if (tokens.ContainsKey(endpoint))

         tokens.Remove(endpoint);

         tokens.Add(endpoint, token);

     }

     finally

     {

       tokenLock.ReleaseWriterLock();

     }

   }

 }

 

 /// <summary>

 /// Custom token provider. This class keeps the tokens outside of the channel

 /// so they can be reused

 /// </summary>

 class CustomIssuedSecurityTokenProvider : IssuedSecurityTokenProvider

 {

   private IssuedSecurityTokenProvider innerProvider;

   /// <summary>

   /// Constructor

   /// </summary>

   public CustomIssuedSecurityTokenProvider(IssuedSecurityTokenProvider innerProvider)

            : base()

   {

      this.innerProvider = innerProvider;

      this.CacheIssuedTokens = innerProvider.CacheIssuedTokens;

      this.IdentityVerifier = innerProvider.IdentityVerifier;

      this.IssuedTokenRenewalThresholdPercentage = innerProvider.IssuedTokenRenewalThresholdPercentage;

      this.IssuerAddress = innerProvider.IssuerAddress;

      this.IssuerBinding = innerProvider.IssuerBinding;

      foreach (IEndpointBehavior behavior in innerProvider.IssuerChannelBehaviors)

      {

        this.IssuerChannelBehaviors.Add(behavior);

      }

      this.KeyEntropyMode = innerProvider.KeyEntropyMode;

      this.MaxIssuedTokenCachingTime = innerProvider.MaxIssuedTokenCachingTime;

      this.MessageSecurityVersion = innerProvider.MessageSecurityVersion;

      this.SecurityAlgorithmSuite = innerProvider.SecurityAlgorithmSuite;

      this.SecurityTokenSerializer = innerProvider.SecurityTokenSerializer;

      this.TargetAddress = innerProvider.TargetAddress;

      foreach (XmlElement parameter in innerProvider.TokenRequestParameters)

      {

        this.TokenRequestParameters.Add(parameter);

      }

      this.innerProvider.Open();

    }                              

 

   /// <summary>

   /// Gets the security token

   /// </summary>

   /// <param name="timeout"></param>

   /// <returns></returns>

   protected override System.IdentityModel.Tokens.SecurityToken GetTokenCore(TimeSpan timeout)

   {

     SecurityToken securityToken = null;

     if (this.CacheIssuedTokens)

     {

       securityToken = TokenCache.GetToken(this.innerProvider.IssuerAddress.Uri);

       if (securityToken == null || !IsServiceTokenTimeValid(securityToken))

       {

         securityToken = innerProvider.GetToken(timeout);

         TokenCache.AddToken(this.innerProvider.IssuerAddress.Uri, securityToken);

       }

     }

     else

     {

       securityToken = innerProvider.GetToken(timeout);

     }

     return securityToken;

   }

 

   /// <summary>

   /// Checks the token expiration.

   /// A more complex algorithm can be used here to determine whether the token is valid or not.

   /// </summary>

   private bool IsServiceTokenTimeValid(SecurityToken serviceToken)

   {

     return (DateTime.UtcNow <= serviceToken.ValidTo.ToUniversalTime());

   }

 

  ~CustomIssuedSecurityTokenProvider()

  {

    this.innerProvider.Close();

  }

 

The provider is quite simple, it caches the tokens by IssuerAddress and checks the token expiration before returning it. When the token is expired, it gets a new token calling the inner token provider.

In order to register the CustomClientCredentials class, the following configuration is required (Using the "type" attribute)

 

<behavior name="ServiceBehavior">

  <clientCredentials type="CustomClientCredentials, MyAssembly">

  </clientCredentials>

</behavior>

 

Posted by cibrax | 7 comment(s)
Filed under:

As most of you know, Pair programming is one of practices included in Extreme Programming. It is a programming technique that provides line-of-sight-and-hearing learning.

I have only applied this technique a few times in the past,  finding it very useful to learn both programming techniques and problem domain.

During the last week I started to work with a very cool guy, Dustin, in an interesting project to build some proof-of-concept applications for his company. Dustin usually reads my blog and he decided to hire me as a kind of consultant or mentor to guide him in the building of those applications.

The main problem in this case was the distance, I am currently living in Argentina and he is in USA. It was impossible to me to travel to USA this month so we decided to use a sort of "Remote Pair Programming" technique.  

We have a meeting almost every day and he shares his screen with me using a tool called TighVNC (A great tool indeed, and it is open source too).

 

I can not say how useful it was, we are making a lot of progress and learning something new every day. I have just wanted to write this post in order to share this experience since I find it very rewarding.

If you know about other tools to do something similar, please, post a comment.

Posted by cibrax | 4 comment(s)
Filed under:

I decided to write this post in order to show some necessary steps to build a Secure Token Service (STS) with the latest WCF CTP.

There is a lot of messages in the newsgroups from people with problems to implement a solution like this, so they may find this article useful.

The image below illustrates a generic architecture for an application that uses a brokered authentication with a STS.

 

 

 

 

 

The client application is using a customBinding to secure the communication with the STS and a wsFederationHttpBinding to do that with the target service. In this case, the wsFederationHttpBinding includes the token obtained from the STS in the request message for the service.

As you can see in the image, the following steps are performed in order to execute the final service:

 

1. The client application sends a RequestSecurityTokenMessage (RST) to the STS according to WS-Trust specification.

2. The STS receives a RST message, extract some information from it and creates a token. After that, it sends back a RequestSecurityTokenResponseMessage (RSTR) with the new token.

3. The client application sends a request message to the service and includes the token obtained from the STS.

4. The service executes the service and returns the response to the client application. The token is used to build the security claims for the authenticated user before calling the service method.

 

 

WCF configuration for the client application

 

 

<system.serviceModel >

        <client>

            <!-- Endpoint configuration -->

            <endpoint name="clientendpoint" address="http://localhost/WCFSampleService/service.svc"

                binding="wsFederationHttpBinding"

                contract="IHelloWorld"

                behaviorConfiguration="ServiceBehavior"

                bindingConfiguration="ServiceBinding">

                <identity>

                    <dns value="WCFQuickstartServer"/>

                </identity>

            </endpoint>

        </client>

 

        <bindings>

 

            <!-- Binding used to secure the communication with the STS -->

            <customBinding>

                <binding name="UsernameBinding">

                    <security authenticationMode="UserNameForCertificate"

                            requireSecurityContextCancellation ="false"

                            requireSignatureConfirmation="false"

                            messageProtectionOrder ="SignBeforeEncryptAndEncryptSignature"

                            requireDerivedKeys="true">

                    </security>

                    <httpTransport/>

                </binding>

            </customBinding>

 

            <!-- Binding used to secure the communication with the service -->

            <wsFederationHttpBinding>

                <binding name="ServiceBinding">

                    <security mode="Message">

                        <message issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" negotiateServiceCredential="false">

                            <!-- Uncomment this section to ask for specific claims to the STS

                            <claims>

                                <add claimType  ="http://schemas.microsoft.com/ws/2005/05/identity/claims/EmailAddress"/>

                                <add claimType  ="http://schemas.microsoft.com/ws/2005/05/identity/claims/GivenName"/>

                                <add claimType  ="http://schemas.microsoft.com/ws/2005/05/identity/claims/Surname"/>

                            </claims>

                            -->

 

                            <!-- Information related to the Secure token service -->

                            <issuer address="http://localhost/WCFSecurityTokenService/service.svc" bindingConfiguration="UsernameBinding"

                                binding="customBinding">

                                <identity>

                                    <dns value="WCFQuickstartServer"/>

                                </identity>

                            </issuer>

                        </message>

                    </security>

                </binding>

            </wsFederationHttpBinding>

        </bindings>

        <behaviors>

            <!-- Credentials configuration -->

            <behavior name="ServiceBehavior">

                <clientCredentials>

                    <serviceCertificate>

                        <defaultCertificate findValue="CN=WCFQuickstartServer" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName"/>

                        <authentication revocationMode="NoCheck" certificateValidationMode="None"></authentication>

                    </serviceCertificate>

                </clientCredentials>

            </behavior>

        </behaviors>

 </system.serviceModel>

 

 

Some notes about the configuration above:

 

1. The communication between the client and the STS is secured by a UsernameForCertificate binding. That is, the STS expects a UsernameToken as client token (Token used to authenticate the client)  and a X509Certificate as service token (Token used to encrypt and sign the message).

2. The "issueTokenType" attribute in the "message" element specifies the token type expected by the Service. The client application will include that value in the RST message and therefore the STS will know what kind of token it must create. If the STS does not support that kind of token, then it will return a fault message. For this sample, the client application is asking for a SAML token.

3. The "negotiateServiceCredential" attribute in the "message" element specifies if the client must interchange additional messages with the STS in order to negotiate the service certificate. I will give more information about this flag later in the next paragraphs.

4. The "address" attribute in the "issuer" element specifies the address of the STS. WCF also includes a default implementation of a InfoCard STS. In order to use that STS, you must configure the address http://schemas.microsoft.com/ws/2005/05/identity/issuer/self.

5. The claims element is only valid for SAML tokens. It specifies what claims are expected in the token.

 

 

WCF configuration for the STS

 

 

<system.serviceModel>

        <services>

            <service behaviorConfiguration="ServiceBehavior" name="MySecureTokenService">

                <endpoint binding="customBinding" address="" bindingConfiguration="ServiceBinding" contract="IMySecureTokenService"></endpoint>

            </service>

        </services>

        <bindings>

            <customBinding>

                <binding name="ServiceBinding">

                    <security authenticationMode="UserNameForCertificate"

                            requireSecurityContextCancellation ="false"

                            requireSignatureConfirmation="false"

                            messageProtectionOrder ="SignBeforeEncryptAndEncryptSignature"

                            requireDerivedKeys="true">

                    </security>

                    <httpTransport/>

                </binding>

            </customBinding>

        </bindings>

        <behaviors>

            <behavior name="ServiceBehavior" returnUnknownExceptionsAsFaults="false">

                <serviceCredentials>

                    <serviceCertificate findValue="CN=WCFQuickstartServer" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName"/>

                    </serviceCredentials>

            </behavior>

        </behaviors>

 </system.serviceModel>

 

 

In this case, the STS implementation is in the class "MySecureTokenService" and it exposes the contract "IMySecureTokenService".

The binding configuration is similar to the configuration in the client.

 

 

WCF configuration for the Service

 

 

<system.serviceModel>

        <services>

            <service

                behaviorConfiguration="ServiceBehavior"

                name="SampleService.HelloWorldService">

                <endpoint binding="wsFederationHttpBinding"

                    address=""

                    bindingConfiguration="ServiceBinding"

                    contract="SampleService.IHelloWorld"/>

            </service>

        </services>

        <bindings>

            <wsFederationHttpBinding>

                <binding name="ServiceBinding">

                    <security mode="Message">

                        <message issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" negotiateServiceCredential="false">

                            <!--<claims>

                                <add claimType="http://schemas.microsoft.com/ws/2005/05/identity/claims/EmailAddress"/>

                                <add claimType="http://schemas.microsoft.com/ws/2005/05/identity/claims/GivenName"/>

                                <add claimType="http://schemas.microsoft.com/ws/2005/05/identity/claims/Surname"/>

                            </claims>-->

                            <issuer address="http://localhost/SamlSecurityTokenService/SamlTokenIssuer.ashx" bindingConfiguration="UsernameBinding" binding="customBinding">

                                <identity>

                                    <dns value="WCFQuickstartServer"/>

                                </identity>

                            </issuer>

                        </message>

                    </security>

                </binding>

            </wsFederationHttpBinding>

        </bindings>

        <behaviors>

            <behavior name="ServiceBehavior" returnUnknownExceptionsAsFaults="false">

                <serviceCredentials>

                    <serviceCertificate findValue="CN=WCFQuickstartServer" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName"/>

                </serviceCredentials>

 

            </behavior>

        </behaviors>

 </system.serviceModel>

 

 

Again, the configuration of the wsFederationHttpBinding is identical to the configuration in the client application.

 

 

STS implementation

 

 

The contract for the STS is quite simple and looks as follows:

 

 

[ServiceContract]

public interface IMySecurityTokenService

{

    [OperationContract(Action = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",

                      ReplyAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue")]

        Message IssueToken(Message rstMessage);

}

 

 

It exposes one method "IssueToken" for the action "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue" that is part of the WS-Trust specification.

 

 

public class MySecureTokenService : IMySecurityTokenService

{

        public MySecureTokenService()

        {

        }

 

        public Message IssueToken(Message rstMessage)

        {

            RequestSecurityToken rst = RequestSecurityToken.CreateFrom(rstMessage.GetReaderAtBodyContents());

 

            SecurityToken issuedToken = null;

 

            //Code to create the token goes here ......

 

            // setup RSTR

            RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse();

 

            //attach security token to RSTR

            rstr.RequestedSecurityToken = issuedToken;

            rstr.TokenType = rst.TokenType;

 

            // send RSTR

            rstr.MakeReadOnly();

            Message rstrMessage = Message.CreateMessage(rstMessage.Version, "http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue", rstr);

            rstrMessage.Headers.RelatesTo = rstMessage.Headers.MessageId;

 

            return rstrMessage;

        }

    }

 

 

The STS implementation receives a generic message containing the RST and creates a token using that information.
At the end, it returns a message containing the RSTR with the issued token. For this sample, I have omitted the code to build the token since you can create any token there (UsernameToken, SamlSecurityToken, etc)  depending on the value of the property "rst.TokenType".

 

 

Avoiding the service credential negotiation

 

WCF provides a new feature to negotiate the service credentials for a service.

When this feature is turned on, the client does not need to manually configure or specify the service credentials for the service. As a result, the client application interchanges an additional message with the service using a a protocol called SP-Nego.

There is not any documentation or information around for that protocol, so it could be a problem if want to host your service in different platform like WSE. That is not a problem in WCF because the security bindings know how to interpret this message and create a response according to its content.

 

There are two ways to disable this feature in WCF:

 

1. Secure the communication with a customBinding since it does not provide this feature.

2. Turn off the attribute "negotiateServiceCredential" in the "message" element for the bindings wsFederationHttpBinding or wsHttpBinding.

 

UPDATE: The STS implementation code is available in this post http://weblogs.asp.net/cibrax/archive/2006/09/08/SAML-_2D00_-STS-implementation-for-WCF.aspx

Posted by cibrax | 32 comment(s)
Filed under: ,

Juval Lowy has posted an updated version of the WCF configuration schema here.

If you want to have intellisence support to modify the WCF configuration files in the February CTP, you will have to download that schema and replace the existing schema in C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas.

This is a temporary fix for a bug in the February CTP.

Posted by cibrax
Filed under:
More Posts