Partilhar via


How to: Create Custom Client and Service Credentials

This topic shows how to implement custom client and service credentials and how to use custom credentials from application code. The system-defined credentials provide functionality to meet most requirements. Custom credentials should only be used when implementing a custom security protocol.

Credentials Extensibility Classes

The ClientCredentials and ServiceCredentials classes are the main entry points to the Windows Communication Foundation (WCF) security extensibility. These credentials classes provide the APIs that enable application code to set credentials information and to convert credential types into security tokens. (Security tokens are the form used to transmit credential information inside SOAP messages.) The responsibilities of these credentials classes can be divided into two areas:

  • Provide the APIs for applications to set credentials information.

  • Perform as a factory for SecurityTokenManager implementations.

Both the ClientCredentials and the ServiceCredentials classes inherit from the abstract SecurityCredentialsManager class that defines the contract for returning the SecurityTokenManager.

For more information about the credentials classes and how they fit into the WCF security architecture, see Security Architecture.

The default implementations provided in WCF support the system-provided credential types and create a security token manager that is capable of handling those credentials types.

Reasons to Customize

There are multiple reasons for customizing either client or service credential classes. Foremost is changing the default WCF security behavior with regard to handling system-provided credential types, especially for these reasons:

  • Changes that are not possible using other extensibility points.

  • Adding new credential types.

  • Adding new custom security token types.

This topic describes how to implement custom client and service credentials and how to use them from application code.

First in a Series

Creating a custom credentials class is only the first step, because the reason for customizing credentials is to change WCF behavior regarding credentials provisioning, security token serialization, or authentication. Other topics in this section describe how to create custom serializers and authenticators. In this regard, creating a custom credential class is the first topic in the series. Subsequent actions (creating custom serializers and authenticators) can be done only after creating custom credentials. Additional topics that build upon this topic include:

Procedures

To implement custom client credentials

  1. Define a new class derived from the ClientCredentials class.

  2. Optional. Add new methods or properties for new credential types. If you do not add new credential types, skip this step. The following example adds a CreditCardNumber property.

  3. Override the CreateSecurityTokenManager method. This method is automatically called by WCF security infrastructure when the custom client credential is used. This method is responsible for creating and returning an instance of an implementation of the SecurityTokenManager class.

    Note

    It is important to note that the CreateSecurityTokenManager method is overridden to create a custom security token manager that provides tokens for each message. The security token manager, derived from ClientCredentialsSecurityTokenManager must return a custom security token provider, derived from SecurityTokenProvider, to create the actual security token. If you do not follow this pattern for creating security tokens, your application may share security tokens between different requests that originate from the same channel factory and is at risk for security attacks, specifically elevation of privilege attacks. This coding pattern ensures that the correct credentials are used for individual requests when ChannelFactory objects are cached.

  4. Override the CloneCore method.

    public class MyClientCredentials : ClientCredentials
    {
        string creditCardNumber;
    
        public MyClientCredentials()
        {
            // Perform client credentials initialization.
        }
    
        protected MyClientCredentials(MyClientCredentials other)
            : base(other)
        {
            // Clone fields defined in this class.
            this.creditCardNumber = other.creditCardNumber;
        }
    
        public string CreditCardNumber
        {
            get
            {
                return this.creditCardNumber;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                this.creditCardNumber = value;
            }
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            // Return your implementation of the SecurityTokenManager.
            return new MyClientCredentialsSecurityTokenManager(this);
        }
    
        protected override ClientCredentials CloneCore()
        {
            // Implement the cloning functionality.
            return new MyClientCredentials(this);
        }
    }
    

To implement custom client security token manager

  1. Define a new class derived from ClientCredentialsSecurityTokenManager.

  2. Override the CreateSecurityTokenProvider method if a custom SecurityTokenProvider implementation must be created. For more information about custom security token providers, see How to: Create a Custom Security Token Provider.

  3. Override the CreateSecurityTokenAuthenticator method if a custom SecurityTokenAuthenticator implementation must be created. For more information about custom security token authenticators, see How to: Create a Custom Security Token Authenticator.

  4. Override the CreateSecurityTokenSerializer method if a custom SecurityTokenSerializer must be created. For more information about custom security tokens and custom security token serializers, see How to: Create a Custom Token.

    internal class MyClientCredentialsSecurityTokenManager : 
        ClientCredentialsSecurityTokenManager
    {
        MyClientCredentials credentials;
    
        public MyClientCredentialsSecurityTokenManager(MyClientCredentials credentials)
            : base(credentials)
        {
            this.credentials = credentials;
        }
    
        public override SecurityTokenProvider CreateSecurityTokenProvider(
            SecurityTokenRequirement tokenRequirement)
        {
            // Return your implementation of the SecurityTokenProvider, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenProvider(tokenRequirement);
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(
            SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            // Return your implementation of the SecurityTokenAuthenticator, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
        }
    
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            // Return your implementation of the SecurityTokenSerializer, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenSerializer(version);
        }
    }
    

To use custom client credentials from application code

  1. Either create an instance of the generated client that represents the service interface, or create an instance of the ChannelFactory that points to a service you want to communicate with.

  2. Remove the system-provided client credentials behavior from the Behaviors collection, which can be accessed through the Endpoint property.

  3. Create a new instance of a custom client credentials class and add it to the Behaviors collection, which can be accessed through the Endpoint property.

    // Create a client with the client endpoint configuration.
    CalculatorClient client = new CalculatorClient();
    
    // Remove the ClientCredentials behavior.
    client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
    
    // Add a custom client credentials instance to the behaviors collection.
    client.ChannelFactory.Endpoint.Behaviors.Add(new MyClientCredentials());
    

The previous procedure shows how to use client credentials from application code. WCF credentials can also be configured using the application configuration file. Using application configuration is often preferable to hard-coding because it enables modification of application parameters without having to modify the source, recompiling, and redeployment.

The next procedure describes how to provide support for configuration of custom credentials.

To create a configuration handler for custom client credentials

  1. Define a new class derived from ClientCredentialsElement.

  2. Optional. Add properties for all additional configuration parameters that you want to expose through application configuration. The following example adds one property named CreditCardNumber.

  3. Override the BehaviorType property to return the type of the custom client credentials class created with the configuration element.

  4. Override the CreateBehavior method. The method is responsible for creating and returning an instance of the custom credential class based on the settings loaded from the configuration file. Call the base ApplyConfiguration method from this method to retrieve the system-provided credentials settings loaded into your custom client credentials instance.

  5. Optional. If you added additional properties in step 2, override the Properties property to register your additional configuration settings for the configuration to recognize them. Combine your properties with the base class properties to allow the system-provided settings to be configured through this custom client credentials configuration element.

    public class MyClientCredentialsConfigHandler : ClientCredentialsElement
    {
        ConfigurationPropertyCollection properties;
    
        public override Type BehaviorType
        {
            get { return typeof(MyClientCredentials); }
        }
    
        public string CreditCardNumber
        {
            get { return (string)base["creditCardNumber"]; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    value = String.Empty;
                }
                base["creditCardNumber"] = value;
            }
        }
    
        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                if (this.properties == null)
                {
                    ConfigurationPropertyCollection properties = base.Properties;
                    properties.Add(new ConfigurationProperty(
                        "creditCardNumber", 
                        typeof(System.String), 
                        string.Empty,
                        null, 
                        new StringValidator(0, 32, null), 
                        ConfigurationPropertyOptions.None));
                    this.properties = properties;
                }
                return this.properties;
            }
        }
    
        protected override object CreateBehavior()
        {
            MyClientCredentials creds = new MyClientCredentials();
            creds.CreditCardNumber = CreditCardNumber;
            base.ApplyConfiguration(creds);
            return creds;
        }
    }
    

Once you have the configuration handler class, it can be integrated into the WCF configuration. That enables the custom client credentials to be used in the client endpoint behavior elements, as shown in the next procedure.

To register and use a custom client credentials configuration handler in the application configuration

  1. Add an <extensions> element and a <behaviorExtensions> element to the configuration file.

  2. Add an <add> element to the <behaviorExtensions> element and set the name attribute to an appropriate value.

  3. Set the type attribute to the fully-qualified type name. Also include the assembly name and other assembly attributes.

    <system.serviceModel>
      <extensions>
        <behaviorExtensions>
          <add name="myClientCredentials" type="Microsoft.ServiceModel.Samples.MyClientCredentialsConfigHandler, CustomCredentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </behaviorExtensions>
      </extensions>
    <system.serviceModel>
    
  4. After registering your configuration handler, the custom credentials element can be used inside the same configuration file instead of the system-provided <clientCredentials> element. You can use both the system-provided properties and any new properties that you have added to your configuration handler implementation. The following example sets the value of a custom property using the creditCardNumber attribute.

    <behaviors>
      <endpointBehaviors>
        <behavior name="myClientCredentialsBehavior">
          <myClientCredentials creditCardNumber="123-123-123"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    

To implement custom service credentials

  1. Define a new class derived from ServiceCredentials.

  2. Optional. Add new properties to provide APIs for new credential values that are being added. If you do not add new credential values, skip this step. The following example adds an AdditionalCertificate property.

  3. Override the CreateSecurityTokenManager method. This method is automatically called by the WCF infrastructure when the custom client credential is used. The method is responsible for creating and returning an instance of an implementation of the SecurityTokenManager class (described in the next procedure).

  4. Optional. Override the CloneCore method. This is required only if adding new properties or internal fields to the custom client credentials implementation.

    public class MyServiceCredentials : ServiceCredentials
    {
        X509Certificate2 additionalCertificate;
    
        public MyServiceCredentials()
        {
        }
    
        protected MyServiceCredentials(MyServiceCredentials other)
            : base(other)
        {
            this.additionalCertificate = other.additionalCertificate;
        }
    
        public X509Certificate2 AdditionalCertificate
        {
            get
            {
                return this.additionalCertificate;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                this.additionalCertificate = value;
            }
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return base.CreateSecurityTokenManager();
        }
    
        protected override ServiceCredentials CloneCore()
        {
            return new MyServiceCredentials(this);
        }
    }
    

To implement a custom service security token manager

  1. Define a new class derived from the ServiceCredentialsSecurityTokenManager class.

  2. Optional. Override the CreateSecurityTokenProvider method if a custom SecurityTokenProvider implementation must be created. For more information about custom security token providers, see How to: Create a Custom Security Token Provider.

  3. Optional. Override the CreateSecurityTokenAuthenticator method if a custom SecurityTokenAuthenticator implementation must be created. For more information about custom security token authenticators, see How to: Create a Custom Security Token Authenticator topic.

  4. Optional. Override the CreateSecurityTokenSerializer method if a custom SecurityTokenSerializer must be created. For more information about custom security tokens and custom security token serializers, see How to: Create a Custom Token.

    internal class MyServiceCredentialsSecurityTokenManager : 
        ServiceCredentialsSecurityTokenManager
    {
        MyServiceCredentials credentials;
    
        public MyServiceCredentialsSecurityTokenManager(MyServiceCredentials credentials)
            : base(credentials)
        {
            this.credentials = credentials;
        }
    
        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            // Return your implementation of SecurityTokenProvider, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenProvider(tokenRequirement);
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            // Return your implementation of SecurityTokenProvider, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
        }
    
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            // Return your implementation of SecurityTokenProvider, if required.
            // This implementation delegates to the base class.
            return base.CreateSecurityTokenSerializer(version);
        }
    }
    

To use custom service credentials from application code

  1. Create an instance of the ServiceHost.

  2. Remove the system-provided service credentials behavior from the Behaviors collection.

  3. Create a new instance of the custom service credentials class and add it to the Behaviors collection.

    // Create a service host with a service type.
    ServiceHost serviceHost = new ServiceHost(typeof(Service));
    
    // Remove the default ServiceCredentials behavior.
    serviceHost.Description.Behaviors.Remove<ServiceCredentials>();
    
    // Add a custom service credentials instance to the collection.
    serviceHost.Description.Behaviors.Add(new MyServiceCredentials());
    

Add support for configuration using the steps previously described in the procedures "To create a configuration handler for custom client credentials" and "To register and use a custom client credentials configuration handler in the application configuration." The only difference is to use the ServiceCredentialsElement class instead of the ClientCredentialsElement class as a base class for the configuration handler. The custom service credential element can then be used wherever the system-provided <serviceCredentials> element is used.

See Also

Tasks

How to: Create a Custom Security Token Provider

Reference

ClientCredentials
ServiceCredentials
SecurityCredentialsManager
SecurityTokenManager
ClientCredentialsElement
ServiceCredentialsElement

Concepts

How to: Create a Custom Security Token Authenticator
How to: Create a Custom Token