Friday 1 May 2020

How to: Create a Custom Security Token Provider

The security token provider creates a security token representation based on information in the client or service credentials. To use the custom security token provider in Windows Communication Foundation (WCF) security, you must create custom credentials and security token manager implementations.

To create a custom security token provider

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


  2. Implement the GetTokenCore(TimeSpan) method. The method is responsible for creating and returning an instance of the security token. The following example creates a class named MySecurityTokenProvider, and overrides the GetTokenCore(TimeSpan) method to return an instance of the X509SecurityToken class. The class constructor requires an instance of the X509Certificate2 class.

    internal class MySecurityTokenProvider : SecurityTokenProvider
    {
        X509Certificate2 certificate;
    
        public MySecurityTokenProvider(X509Certificate2 certificate)
        {
            this.certificate = certificate;
        }
    
        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            return new X509SecurityToken(certificate);
        }
    }
    

To integrate a custom security token provider with a custom security token manager

  1. Define a new class derived from the SecurityTokenManager class. (The example below derives from the ClientCredentialsSecurityTokenManager class, which derives from the SecurityTokenManager class.)

  2. Override the CreateSecurityTokenProvider(SecurityTokenRequirement) method if is not already overridden.

    The CreateSecurityTokenProvider(SecurityTokenRequirement) method is responsible for returning an instance of the SecurityTokenProvider class appropriate to the SecurityTokenRequirement parameter passed to the method by the WCF security framework. Modify the method to return the custom security token provider implementation (created in the previous procedure) when the method is called with an appropriate security token parameter. For more information about the security token manager, see the Walkthrough: Creating Custom Client and Service Credentials.

  3. Add custom logic to the method to enable it to return your custom security token provider based on the SecurityTokenRequirement parameter. The following sample returns the custom security token provider if the token requirements are met. The requirements include an X.509 security token and the message direction (that the token is used for message output). For all other cases, the code calls the base class to maintain the system-provided behavior for other security token requirements.

C#
internal class MyClientCredentialsSecurityTokenManager:ClientCredentialsSecurityTokenManager
{
    ClientCredentials credentials;

    public MyClientCredentialsSecurityTokenManager(ClientCredentials credentials)
        : base(credentials)
    {
        this.credentials = credentials;
    }

    public override SecurityTokenProvider CreateSecurityTokenProvider(
        SecurityTokenRequirement tokenRequirement)
    {
        // Return your implementation of the SecurityTokenProvider based on the
        // tokenRequirement argument.
        SecurityTokenProvider result;
        if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
        {
            MessageDirection direction = tokenRequirement.GetProperty<MessageDirection>(
                ServiceModelSecurityTokenRequirement.MessageDirectionProperty);
            if (direction == MessageDirection.Output)
            {
                result = new MySecurityTokenProvider(credentials.ClientCertificate.Certificate);
            }
            else
            {
                result = base.CreateSecurityTokenProvider(tokenRequirement);
            }
        }
        else
        {
            result = base.CreateSecurityTokenProvider(tokenRequirement);
        }

        return result;
    }
}

Example

The following shows a complete SecurityTokenProvider implementation along with a corresponding SecurityTokenManager implementation.

C#
using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Permissions;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security.Tokens;

[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]
namespace CustomProvider
{
    internal class MySecurityTokenProvider : SecurityTokenProvider
    {
        X509Certificate2 certificate;

        public MySecurityTokenProvider(X509Certificate2 certificate)
        {
            this.certificate = certificate;
        }

        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            return new X509SecurityToken(certificate);
        }
    }

    internal class MyClientCredentialsSecurityTokenManager:ClientCredentialsSecurityTokenManager
    {
        ClientCredentials credentials;

        public MyClientCredentialsSecurityTokenManager(ClientCredentials credentials)
            : base(credentials)
        {
            this.credentials = credentials;
        }

        public override SecurityTokenProvider CreateSecurityTokenProvider(
            SecurityTokenRequirement tokenRequirement)
        {
            // Return your implementation of the SecurityTokenProvider based on the
            // tokenRequirement argument.
            SecurityTokenProvider result;
            if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
            {
                MessageDirection direction = tokenRequirement.GetProperty<MessageDirection>(
                    ServiceModelSecurityTokenRequirement.MessageDirectionProperty);
                if (direction == MessageDirection.Output)
                {
                    result = new MySecurityTokenProvider(credentials.ClientCertificate.Certificate);
                }
                else
                {
                    result = base.CreateSecurityTokenProvider(tokenRequirement);
                }
            }
            else
            {
                result = base.CreateSecurityTokenProvider(tokenRequirement);
            }

            return result;
        }
    }
}

No comments:

Post a Comment