This topic shows how to implement custom client and service credentials and how to use custom credentials from application code.
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:
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.
There are multiple reasons for customizing either client or service credential classes. Foremost is the requirement to change the default WCF security behavior with regard to handling system-provided credential types, especially for the following 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.
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 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:
To implement custom client credentials
Define a new class derived from the ClientCredentials class.
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.
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.
Important
It is important to note that the CreateSecurityTokenManager method is overridden to create a custom security token manager. 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 function incorrectly when ChannelFactory objects are cached (which is the default behavior for WCF client proxies), potentially resulting in an elevation of privilege attack. The custom credential object is cached as part of the ChannelFactory. However, the custom SecurityTokenManager is created on every invocation, which mitigates the security threat as long as the token creation logic is placed in the SecurityTokenManager.
Override the CloneCore method.
public class MyClientCredentials : ClientCredentials
{
string creditCardNumber;
public MyClientCredentials()
{
}
protected MyClientCredentials(MyClientCredentials other)
: base(other)
{
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 new MyClientCredentialsSecurityTokenManager(this);
}
protected override ClientCredentials CloneCore()
{
return new MyClientCredentials(this);
}
}
Public Class MyClientCredentials
Inherits ClientCredentials
Private creditCardNumberValue As String
Public Sub New()
End Sub
' Perform client credentials initialization.
Protected Sub New(ByVal other As MyClientCredentials)
MyBase.New(other)
' Clone fields defined in this class.
Me.creditCardNumberValue = other.creditCardNumberValue
End Sub
Public Property CreditCardNumber() As String
Get
Return Me.creditCardNumberValue
End Get
Set
If value Is Nothing Then
Throw New ArgumentNullException("value")
End If
Me.creditCardNumberValue = value
End Set
End Property
Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager
' Return your implementation of the SecurityTokenManager.
Return New MyClientCredentialsSecurityTokenManager(Me)
End Function
Protected Overrides Function CloneCore() As ClientCredentials
' Implement the cloning functionality.
Return New MyClientCredentials(Me)
End Function
End Class
To implement a custom client security token manager
Define a new class derived from ClientCredentialsSecurityTokenManager.
Optional. Override the CreateSecurityTokenProvider(SecurityTokenRequirement) 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.
Optional. Override the CreateSecurityTokenAuthenticator(SecurityTokenRequirement, SecurityTokenResolver) 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.
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 MyClientCredentialsSecurityTokenManager :
ClientCredentialsSecurityTokenManager
{
MyClientCredentials credentials;
public MyClientCredentialsSecurityTokenManager(MyClientCredentials credentials)
: base(credentials)
{
this.credentials = credentials;
}
public override SecurityTokenProvider CreateSecurityTokenProvider(
SecurityTokenRequirement tokenRequirement)
{
return base.CreateSecurityTokenProvider(tokenRequirement);
}
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(
SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return base.CreateSecurityTokenSerializer(version);
}
}
Friend Class MyClientCredentialsSecurityTokenManager
Inherits ClientCredentialsSecurityTokenManager
Private credentials As MyClientCredentials
Public Sub New(ByVal credentials As MyClientCredentials)
MyBase.New(credentials)
Me.credentials = credentials
End Sub
Public Overrides Function CreateSecurityTokenProvider( _
ByVal tokenRequirement As SecurityTokenRequirement) As SecurityTokenProvider
' Return your implementation of the SecurityTokenProvider, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenProvider(tokenRequirement)
End Function
Public Overrides Function CreateSecurityTokenAuthenticator( _
ByVal tokenRequirement As SecurityTokenRequirement, _
ByRef outOfBandTokenResolver As SecurityTokenResolver) As SecurityTokenAuthenticator
' Return your implementation of the SecurityTokenAuthenticator, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, outOfBandTokenResolver)
End Function
Public Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) _
As SecurityTokenSerializer
' Return your implementation of the SecurityTokenSerializer, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenSerializer(version)
End Function
End Class
To use a custom client credentials from application code
Either create an instance of the generated client that represents the service interface, or create an instance of the ChannelFactory pointing to a service you want to communicate with.
Remove the system-provided client credentials behavior from the Behaviors collection, which can be accessed through the Endpoint property.
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.
CalculatorClient client = new CalculatorClient();
client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
client.ChannelFactory.Endpoint.Behaviors.Add(new MyClientCredentials());
' Create a client with the client endpoint configuration.
Dim client As New CalculatorClient()
' Remove the ClientCredentials behavior.
client.ChannelFactory.Endpoint.Behaviors.Remove(Of 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.
Creating a configuration handler for custom client credentials
Define a new class derived from ClientCredentialsElement.
Optional. Add properties for all additional configuration parameters that you want to expose through application configuration. The example below adds one property named CreditCardNumber
.
Override the BehaviorType property to return the type of the custom client credentials class created with the configuration element.
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(ClientCredentials) method from this method to retrieve the system-provided credentials settings loaded into your custom client credentials instance.
Optional. If you added additional properties in step 2, you need to override the Properties property in order to register your additional configuration settings for the configuration framework 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;
}
}
Public Class MyClientCredentialsConfigHandler
Inherits ClientCredentialsElement
Private propertiesValue As ConfigurationPropertyCollection
Public Overrides ReadOnly Property BehaviorType() As Type
Get
Return GetType(MyClientCredentials)
End Get
End Property
Public Property CreditCardNumber() As String
Get
Return CStr(MyBase.Item("creditCardNumber"))
End Get
Set
If String.IsNullOrEmpty(value) Then
value = String.Empty
End If
MyBase.Item("creditCardNumber") = value
End Set
End Property
Protected Overrides ReadOnly Property Properties() As ConfigurationPropertyCollection
Get
If Me.propertiesValue Is Nothing Then
Dim myProperties As ConfigurationPropertyCollection = MyBase.Properties
myProperties.Add(New ConfigurationProperty( _
"creditCardNumber", _
GetType(System.String), _
String.Empty, _
Nothing, _
New StringValidator(0, 32, Nothing), _
ConfigurationPropertyOptions.None))
Me.propertiesValue = myProperties
End If
Return Me.propertiesValue
End Get
End Property
Protected Overrides Function CreateBehavior() As Object
Dim creds As New MyClientCredentials()
creds.CreditCardNumber = Me.CreditCardNumber
MyBase.ApplyConfiguration(creds)
Return creds
End Function
End Class
Once you have the configuration handler class, it can be integrated into the WCF configuration framework. 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
Add an <extensions>
element and a <behaviorExtensions>
element to the configuration file.
Add an <add>
element to the <behaviorExtensions>
element and set the name
attribute to an appropriate value.
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>
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
Define a new class derived from ServiceCredentials.
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.
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).
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);
}
}
Public Class MyServiceCredentials
Inherits ServiceCredentials
Private additionalCertificateValue As X509Certificate2
Public Sub New()
End Sub
Protected Sub New(ByVal other As MyServiceCredentials)
MyBase.New(other)
Me.additionalCertificate = other.additionalCertificate
End Sub
Public Property AdditionalCertificate() As X509Certificate2
Get
Return Me.additionalCertificateValue
End Get
Set
If value Is Nothing Then
Throw New ArgumentNullException("value")
End If
Me.additionalCertificateValue = value
End Set
End Property
Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager
Return MyBase.CreateSecurityTokenManager()
End Function
Protected Overrides Function CloneCore() As ServiceCredentials
Return New MyServiceCredentials(Me)
End Function
End Class
To implement a custom service security token manager
Define a new class derived from the ServiceCredentialsSecurityTokenManager class.
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.
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.
Optional. Override the CreateSecurityTokenSerializer(SecurityTokenVersion) 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 base.CreateSecurityTokenProvider(tokenRequirement);
}
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return base.CreateSecurityTokenSerializer(version);
}
}
Friend Class MyServiceCredentialsSecurityTokenManager
Inherits ServiceCredentialsSecurityTokenManager
Private credentials As MyServiceCredentials
Public Sub New(ByVal credentials As MyServiceCredentials)
MyBase.New(credentials)
Me.credentials = credentials
End Sub
Public Overrides Function CreateSecurityTokenProvider(ByVal tokenRequirement As SecurityTokenRequirement) _
As SecurityTokenProvider
' Return your implementation of SecurityTokenProvider, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenProvider(tokenRequirement)
End Function
Public Overrides Function CreateSecurityTokenAuthenticator( _
ByVal tokenRequirement As SecurityTokenRequirement, _
ByRef outOfBandTokenResolver As SecurityTokenResolver) _
As SecurityTokenAuthenticator
' Return your implementation of SecurityTokenProvider, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, outOfBandTokenResolver)
End Function
Public Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) _
As SecurityTokenSerializer
' Return your implementation of SecurityTokenProvider, if required.
' This implementation delegates to the base class.
Return MyBase.CreateSecurityTokenSerializer(version)
End Function
End Class
To use custom service credentials from application code
Create an instance of the ServiceHost.
Remove the system-provided service credentials behavior from the Behaviors collection.
Create a new instance of the custom service credentials class and add it to the Behaviors collection.
ServiceHost serviceHost = new ServiceHost(typeof(Service));
serviceHost.Description.Behaviors.Remove<ServiceCredentials>();
serviceHost.Description.Behaviors.Add(new MyServiceCredentials());
' Create a service host with a service type.
Dim serviceHost As New ServiceHost(GetType(Service))
' Remove the default ServiceCredentials behavior.
serviceHost.Description.Behaviors.Remove(Of ServiceCredentials)()
' Add a custom service credentials instance to the collection.
serviceHost.Description.Behaviors.Add(New MyServiceCredentials())
Add support for configuration using the steps described previously 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.