Share via


How to: Create a Class Representing a Custom Binary Security Token

The first step to using custom binary security credentials within a SOAP message is to create a class that represents the custom binary security token, as shown in this topic. After that class is created, create another class that represents a security token manager must be created. For more information about creating a security token manager, see How to: Create a Security Token Manager for a Custom Security Token.

WSE 3.0 allows a security token service to change the type of security token that is being issued without having to update clients when the security token is an opaque token. Previous versions of WSE required the client to be updated to accommodate a different type of issued security token. Specifically, WSE 2.0 required a client application to register all security token managers for issued security tokens; with WSE 3.0, that is no longer necessary. For more details about creating an opaque security token, see SAML STS.

The following procedures create a class that represents a custom binary security token:

  1. Create a class that derives from the BinarySecurityToken class.
  2. Apply an appropriate security permission attribute.
  3. Implement constructors with parameters that reflect your token's data.
  4. Override the appropriate methods and properties of your class.

To create a class that derives from the BinarySecurityToken class

  1. The following procedure creates a custom binary security token for the AES algorithm. To create a custom binary security token for your custom credentials, replace the code in the class with code that is specific to your custom credentials.

  2. Create a new Class Library project in Visual Studio .NET 2003.

  3. Add references to the Micrsosoft.Web.Services, System.Web.Services, System.Security, and System.Xml assemblies.

    1. In Solution Explorer, right-click References, and then click Add Reference.
    2. Click the .NET tab, press and hold down the CTRL key, click Microsoft.Web.Services3.dll, System.Web.Services.dll, System.Security.dll, and System.Xml.dll, and then click Select.
    3. Click OK.
  4. Add the Imports or using directives shown in the following code example to the top of the file for Class1.cs.

    Imports System
    Imports System.Security.Cryptography
    Imports System.Security.Permissions
    Imports System.Text
    Imports System.Globalization
    Imports System.Xml
    Imports System.Configuration
    
    Imports Microsoft.Web.Services3.Addressing
    Imports Microsoft.Web.Services3.Design
    Imports Microsoft.Web.Services3.Security
    Imports Microsoft.Web.Services3.Security.Cryptography
    Imports Microsoft.Web.Services3.Security.Utility
    Imports Microsoft.Web.Services3.Security.Tokens
    
    using System;
    using System.Security.Cryptography;
    using System.Security.Permissions;
    using System.Text;
    using System.Globalization;
    using System.Xml;
    using System.Configuration;
    
    using Microsoft.Web.Services3.Addressing ;
    using Microsoft.Web.Services3.Design;
    using Microsoft.Web.Services3.Security;
    using Microsoft.Web.Services3.Security.Cryptography;
    using Microsoft.Web.Services3.Security.Utility;
    using Microsoft.Web.Services3.Security.Tokens;
    
  5. Add a class that derives from the BinarySecurityToken class.

    <SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _ 
    Public Class BinaryToken
        Inherits BinarySecurityToken
    
    public class BinaryToken : BinarySecurityToken, IIssuedToken
    

To apply an appropriate security permission attribute

  • Apply the SecurityPermissionAttribute attribute to your class to help prevent an arbitrary assembly from accessing your token.

    It is recommended that you demand that an assembly accessing this class already have permission to call unmanaged code. This helps prevent partially trusted code from gaining access to the cryptographic key information that the security token provides.

    The following code example applies the SecurityPermissionAttribute attribute to the class derived from the BinarySecurityToken class, demanding the UnmanagedCode permission.

    <SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _ 
    Public Class BinaryToken
        Inherits BinarySecurityToken
    
    [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] 
    public class BinaryToken : BinarySecurityToken, IIssuedToken
    

To implement constructors with parameters that reflect your token's data

  1. Add one or more constructors that take the data necessary to represent your custom security credentials.

    This constructor is called when a message is received that includes data encrypted with the token. After this constructor runs, the token's Key property is able to decrypt the data.

    ' Life time of this token.
    Private _lifeTime As LifeTime = Nothing
    ' The encrypted form of the key for the target service.
    Private _encryptedKey As EncryptedKey = Nothing
    ' The encrypted form of the key for the token requestor.
    Private _proofToken As RequestedProofToken = Nothing
    ' The key for the token. This will be an AES128 key.
    Private _key As SymmetricKeyAlgorithm = Nothing
    
    ' <summary>
    ' The security token manager uses this constructor to instantiate
    ' a security token from KeyInfo clauses.
    ' </summary>
    Public Sub New(ByVal serviceToken As SecurityToken)
        MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
    
        ' The token we issue will expire after 8 hours.
        _lifeTime = New LifeTime(DateTime.Now, 8 * 60 * 60)
    
        ' Now generate a key.
        _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
        _key.GenerateKey()
    
        ' Generate the encrypted form of the key.
        _encryptedKey = New EncryptedKey(serviceToken, _key.KeyBytes)
    
        ' Set the RawData property to the UTF8Encoding of the Token. 
        Dim tokenString As String = GetXmlString()
        RawData = (New UTF8Encoding).GetBytes(tokenString)
    End Sub
    
    // Life time of this token.
    private LifeTime              _lifeTime     = null;
    // The encrypted form of the key for the target service.
    private EncryptedKey          _encryptedKey = null;
    // The encrypted form of the key for the token requestor.
    private RequestedProofToken   _proofToken   = null;
    // The key for the token. This will be an AES128 key.
    private SymmetricKeyAlgorithm _key          = null;
    
    /// <summary>
    /// The security token manager uses this constructor to instantiate
    /// a security token from KeyInfo clauses.
    /// </summary>
    public BinaryToken(SecurityToken serviceToken) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) 
    {
        // The token we issue will expire after 8 hours.
        _lifeTime = new LifeTime(DateTime.Now, 8 * 60 * 60 );
    
        // Now generate a key.
        _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
        _key.GenerateKey();
    
        // Generate the encrypted form of the key.
        _encryptedKey = new EncryptedKey(serviceToken,_key.KeyBytes);
    
        // Set the RawData property to the UTF8Encoding of the Token. 
        string tokenString = GetXmlString();
        RawData = (new UTF8Encoding()).GetBytes(tokenString);
    }
    
  2. Implement a constructor that takes an XmlElement argument.

    WSE calls this constructor when a SOAP message is received that contains your custom security binary token in the WS-Security SOAP header. The XmlElement argument is the <BinarySecurityToken> element within a WS-Security SOAP header from a received message. This constructor has a default implementation in the BinarySecurityToken class that loads known XML, such as the value of the RawData property.

    The following code calls the base class constructor, which calls the LoadXml method that populates the RawData property if it exists in the XML.

    ' This constructor is called by WSE when a SOAP message is received that contains
    ' a Binary Security Token with a ValueType element equal the ValueType property for this
    ' class. The ValueType property is Server-side constructor. It invokes the base class, which 
    ' then loads all properties from the supplied XmlElement
    ' using LoadXml(XmlElement).
    Public Sub New(ByVal element As XmlElement)
        MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
    
        ' Load the Token from the XmlElement.
        LoadXml(element)
    End Sub
    
    // This constructor is called by WSE when a SOAP message is received that contains
    // a Binary Security Token with a ValueType element equal the ValueType property for this
    // class. The ValueType property is Server-side constructor. It invokes the base class, which 
    // then loads all properties from the supplied XmlElement
    // using LoadXml(XmlElement).
    public BinaryToken(XmlElement element) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
    {
        // Load the Token from the XmlElement.
        LoadXml(element);
    }
    

To override the appropriate methods and properties of your class

  1. Override the LoadXml and GetXml methods.

    The LoadXml method is called by the default implementation of the constructor that accepts an XmlElement argument. The default implementation of the LoadXml method can parse the contents of the <BinarySecurityToken> element into the RawData, ValueType, and EncodingType properties. The default implementation does not, however, know what is stored in the RawData element; so, if a custom binary security token adds unique data to the RawData element, override the LoadXml method to parse out that unique data.

    The GetXml method is called by WSE when the custom binary security token is serialized into the SOAP message. For most cases, the default implementation is sufficient.

    The following code example retrieves the sender's public key from the RawData element. The following code example does not override the GetXml method, but rather creates a GetXmlString method, so that a developer for a sender can retrieve an XML string of the sender's public key.

    Public Overrides Sub LoadXml(ByVal element As XmlElement)
        ' The default implementation parses the required elements for the
        ' <BinarySecurityToken> element and assigns them to their respective 
        ' properties in the BinarySecurityToken class. These properties are
        ' RawData, ValueType, and EncodingType.
        MyBase.LoadXml(element)
    
        ' Reset the existing contents.
        _lifeTime = Nothing
        _key = Nothing
        _encryptedKey = Nothing
        _proofToken = Nothing
    
        ' Check to see if any contents in the RawData element
        ' were assigned to the RawData property by the base class LoadXml
        ' method. If RawData is not null, then the sender's public and private
        ' key pair is contained within it.
        If (Not RawData Is Nothing) Then
            ' The sender's key is stored in the RawData
            Dim tokenString As String = (New UTF8Encoding).GetString(RawData)
            Dim document As XmlDocument = New XmlDocument
            document.LoadXml(tokenString)
            Dim tokenElement As XmlElement = document.DocumentElement
    
            Dim createdAt As DateTime = DateTime.MinValue
            Dim expiresAt As DateTime = DateTime.MaxValue
    
            Select Case tokenElement.LocalName
                Case BinaryTokenNames.TokenName
                    Dim child As XmlNode
                    For Each child In tokenElement.ChildNodes
                        Select Case child.LocalName
                            Case BinaryTokenNames.CreatedAt
                                createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
    
                            Case BinaryTokenNames.ExpiresAt
                                expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
    
                            Case BinaryTokenNames.BinaryKey
                                _encryptedKey = New EncryptedKey(CType(child.FirstChild, XmlElement))
                        End Select
                    Next
            End Select
    
            If (createdAt <> DateTime.MinValue OrElse _
                expiresAt <> DateTime.MaxValue) Then
                _lifeTime = New LifeTime(createdAt, expiresAt)
            End If
        End If
    End Sub
    
    Public Function GetXmlString() As String
        Dim document As XmlDocument = New XmlDocument
        Dim element As XmlElement = document.CreateElement(BinaryTokenNames.TokenName)
    
        '
        ' LifeTime
        '
        If (Not _lifeTime Is Nothing AndAlso _
            _lifeTime.Created <> DateTime.MinValue) Then
            Dim createdAt As XmlElement = document.CreateElement(BinaryTokenNames.CreatedAt)
            createdAt.InnerText = XmlConvert.ToString( _
                _lifeTime.Created.ToUniversalTime(), _
                WSUtility.TimeFormat)
            element.AppendChild(createdAt)
        End If
    
        '
        ' ExpiresAt
        '
        If (Not _lifeTime Is Nothing AndAlso _
            _lifeTime.Expires <> DateTime.MaxValue) Then
            Dim expiresAt As XmlElement = document.CreateElement(BinaryTokenNames.ExpiresAt)
            expiresAt.InnerText = XmlConvert.ToString( _
                _lifeTime.Expires.ToUniversalTime(), _
                WSUtility.TimeFormat)
            element.AppendChild(expiresAt)
        End If
    
        ' 
        ' Binary Key. The binary key is carried within the token as an EncryptedKey. 
        '
        If (Not _encryptedKey Is Nothing) Then
            Dim binaryKey As XmlElement = document.CreateElement(BinaryTokenNames.BinaryKey)
            binaryKey.AppendChild(_encryptedKey.GetXml(document))
            element.AppendChild(binaryKey)
        End If
    
        Return element.OuterXml
    End Function
    
    public override void LoadXml(XmlElement element)
    {
        // The default implementation parses the required elements for the
        // <BinarySecurityToken> element and assigns them to their respective 
        // properties in the BinarySecurityToken class. These properties are
        // RawData, ValueType, and EncodingType.
        base.LoadXml(element);
    
        // Reset the existing contents.
        _lifeTime       = null;
        _key            = null;
        _encryptedKey   = null;
        _proofToken     = null;
    
        // Check to see if any contents in the RawData element
        // were assigned to the RawData property by the base class LoadXml
        // method. If RawData is not null, then the sender's public and private
        // key pair is contained within it.
        if (RawData != null)
        {
            // The sender's key is stored in the RawData
            string tokenString = (new UTF8Encoding()).GetString(RawData);
            XmlDocument document = new XmlDocument();
            document.LoadXml(tokenString);
            XmlElement tokenElement = document.DocumentElement;
    
            DateTime createdAt = DateTime.MinValue;
            DateTime expiresAt = DateTime.MaxValue;
    
            switch ( tokenElement.LocalName ) 
            {
                case BinaryTokenNames.TokenName:
                {
                    foreach(XmlNode child in tokenElement.ChildNodes ) 
                    {
                        switch ( child.LocalName ) 
                        {
                            case BinaryTokenNames.CreatedAt:
                                createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
                                break;
    
                            case BinaryTokenNames.ExpiresAt:
                                expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);                                
                                break;
    
                            case BinaryTokenNames.BinaryKey:
                                _encryptedKey = new EncryptedKey(child.FirstChild as XmlElement);
                                break;
    
                            default:
                                break;                                    
                        }
                    }
                    break;
                }
            }
    
            if ( createdAt != DateTime.MinValue || expiresAt != DateTime.MaxValue )
                _lifeTime = new LifeTime(createdAt, expiresAt);
        }
    }
    
    public string GetXmlString()
    {
        XmlDocument document = new XmlDocument();
        XmlElement  element = document.CreateElement(BinaryTokenNames.TokenName);
    
        //
        // LifeTime
        //
        if (_lifeTime != null && _lifeTime.Created != DateTime.MinValue)
        {
            XmlElement createdAt = document.CreateElement(BinaryTokenNames.CreatedAt);
            createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat);
            element.AppendChild(createdAt);
        }
    
        //
        // ExpiresAt
        //
        if (_lifeTime != null && _lifeTime.Expires != DateTime.MaxValue)
        {
            XmlElement expiresAt = document.CreateElement(BinaryTokenNames.ExpiresAt);
            expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat);
            element.AppendChild(expiresAt);
        }
    
        // 
        // Binary Key. The binary key is carried within the token as an EncryptedKey. 
        //
        if (_encryptedKey != null )
        {
            XmlElement binaryKey = document.CreateElement(BinaryTokenNames.BinaryKey);
            binaryKey.AppendChild(_encryptedKey.GetXml(document));
            element.AppendChild(binaryKey);
        }
    
        return element.OuterXml;
    }
    
  2. Override the SupportsDigitalSignature and SupportsDataEncryption properties.

    These Boolean properties indicate whether the custom binary security can be used for digital signatures and data encryption.

    The following code example specifies that the custom binary security token can be used for all these purposes.

    Public Overrides ReadOnly Property SupportsDigitalSignature() As Boolean
        Get
            Return True
        End Get
    End Property
    
    ' <summary>
    ' This token supports data encryption.
    ' </summary>
    Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean
        Get
            Return True
        End Get
    End Property
    
    public override bool SupportsDigitalSignature
    {
        get
        {
            return true;
        }
    }
    
    /// <summary>
    /// This token supports data encryption.
    /// </summary>
    public override bool SupportsDataEncryption
    {
        get
        {
            return true;
        }
    }
    
  3. Override the Key property.

    The Key property is accessed by WSE to specify the key that is used to perform signing and encryption operations on a SOAP message. This property is of type KeyAlgorithm and is either a SymmetricKeyAlgorithm or an AsymmetricKeyAlgorithm.

    The following code example returns a SymmetricAlgorithm for a 128-bit AES key.

    Public Overrides ReadOnly Property Key() As KeyAlgorithm
        Get
            If (_key Is Nothing) Then
                If (Not _proofToken Is Nothing) Then
                    '
                    ' Attempt recovery from the proof token.
                    '
                    _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                    _key.KeyBytes = _proofToken.KeyBytes
                ElseIf (Not _encryptedKey Is Nothing) Then
                    '
                    ' Attempt key recovery from the encrypted form.
                    '
                    _encryptedKey.Decrypt()
    
                    _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                    _key.KeyBytes = _encryptedKey.KeyBytes
                End If
            End If
    
            Return _key
        End Get
    End Property
    
    public override KeyAlgorithm Key
    {
        get
        {
            if ( _key == null )
            {
                if ( _proofToken != null )
                {
                    //
                    // Attempt recovery from the proof token.
                    //
                    _key          = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                    _key.KeyBytes = _proofToken.KeyBytes;
                }
                else if ( _encryptedKey != null )
                {
                    //
                    // Attempt key recovery from the encrypted form.
                    //
                    _encryptedKey.Decrypt();
    
                    _key          = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                    _key.KeyBytes = _encryptedKey.KeyBytes;
                }
            }
    
            return _key;
        }
    }
    
  4. Override the IsCurrent, RequestedProofToken, and LifeTime properties.

    The following code example specifies that the security token is valid.

    ' <summary>
    ' Return true if the token has not expired and its
    ' creation time is not a postdated time.
    ' </summary>
    Public Overrides ReadOnly Property IsCurrent() As Boolean
        Get
            Return (LifeTime Is Nothing OrElse _
                LifeTime.IsCurrent)
        End Get
    End Property
    
    ' <summary>
    ' Get/Set a life time for this binary token, including creation
    ' time and expiration time.
    ' </summary>
    Public Property LifeTime() As LifeTime Implements IIssuedToken.LifeTime
        Get
            Return _lifeTime
        End Get
        Set(ByVal Value As LifeTime)
            _lifeTime = Value
        End Set
    End Property
    
    ' <summary>
    ' Get/Set the proof token for this binary token. This is the 
    ' encrypted form of the key for the token requestor.
    ' </summary>
    Public Property ProofToken() As RequestedProofToken Implements IIssuedToken.ProofToken
        Get
            Return _proofToken
        End Get
        Set(ByVal Value As RequestedProofToken)
            _proofToken = Value
        End Set
    End Property
    
    /// <summary>
    /// Return true if the token has not expired and its
    /// creation time is not a postdated time.
    /// </summary>
    public override bool IsCurrent
    {
        get
        {
            return (LifeTime == null || LifeTime.IsCurrent);
        }
    }
    
    /// <summary>
    /// Get/Set a life time for this binary token, including creation
    /// time and expiration time.
    /// </summary>
    public LifeTime LifeTime
    {
        get
        {
            return _lifeTime;
        }
        set
        {
            _lifeTime = value;
        }
    }
    
    /// <summary>
    /// Get/Set the proof token for this binary token. This is the 
    /// encrypted form of the key for the token requestor.
    /// </summary>
    public RequestedProofToken ProofToken
    {
        get
        {
            return _proofToken;
        }
        set
        {
            _proofToken = value;
        }
    }
    
  5. Override the GetHashCode and Equals methods.

    The GetHashCode method serves as a hash function for the custom binary security token when the type is added to a Hashtable collection. Override the Equals method so that the Hashtable collection works correctly. The Equals method guarantees that two objects that are considered equal have the same hash code. For more information about these two methods, see Object.GetHashCode.

    The following code example creates a hash code based on the security token's key.

    ' <summary>
    ' Return true if two tokens have the same raw data.
    ' </summary>
    Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
        If (token Is Nothing OrElse _
            Not token.GetType() Is Me.GetType()) Then
            Return False
        Else
            Dim t As BinaryToken = CType(token, BinaryToken)
            If (Not RawData Is Nothing AndAlso _
                Not t.RawData Is Nothing AndAlso _
                RawData.Length = t.RawData.Length) Then
                Dim index As Integer = RawData.Length
                While (--index > -1)
                    If (RawData(index) <> t.RawData(index)) Then
                        Return False
                    End If
                End While
                Return True
            ElseIf (RawData Is Nothing AndAlso _
                t.RawData Is Nothing) Then
                Return True
            Else
                Return False
            End If
        End If
    End Function
    
    ' <summary>
    ' Return the hash of the raw data for this token.
    ' </summary>
    Public Overrides Function GetHashCode() As Integer
        If (Not RawData Is Nothing) Then
            Return RawData.GetHashCode()
        Else
            Return 0
        End If
    End Function
    
    /// <summary>
    /// Return true if two tokens have the same raw data.
    /// </summary>
    public override bool Equals(SecurityToken token)
    {
        if ( token == null || token.GetType() != GetType() )
            return false;
        else
        {
            BinaryToken t = (BinaryToken)token;
            if (RawData != null && t.RawData != null &&
                RawData.Length == t.RawData.Length)
            {
                int index = RawData.Length;
                while (--index > -1)
                    if (RawData[index] != t.RawData[index])
                        return false;
                return true;
            }
            else if (RawData == null && t.RawData == null)
                return true;
            else
                return false;
        }
    }
    
    /// <summary>
    /// Return the hash of the raw data for this token.
    /// </summary>
    public override int GetHashCode()
    {
        if ( RawData != null )
            return RawData.GetHashCode();
        else
            return 0;
    }
    

Example

The following code example is a complete implementation of a custom binary token.

Imports System
Imports System.Security.Cryptography
Imports System.Security.Permissions
Imports System.Text
Imports System.Globalization
Imports System.Xml
Imports System.Configuration

Imports Microsoft.Web.Services3.Addressing
Imports Microsoft.Web.Services3.Design
Imports Microsoft.Web.Services3.Security
Imports Microsoft.Web.Services3.Security.Cryptography
Imports Microsoft.Web.Services3.Security.Utility
Imports Microsoft.Web.Services3.Security.Tokens

Namespace CustomBinaryTokenCode
    <SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _ 
    Public Class BinaryToken
        Inherits BinarySecurityToken
        Implements IIssuedToken
        ' Life time of this token.
        Private _lifeTime As LifeTime = Nothing
        ' The encrypted form of the key for the target service.
        Private _encryptedKey As EncryptedKey = Nothing
        ' The encrypted form of the key for the token requestor.
        Private _proofToken As RequestedProofToken = Nothing
        ' The key for the token. This will be an AES128 key.
        Private _key As SymmetricKeyAlgorithm = Nothing

        ' <summary>
        ' The security token manager uses this constructor to instantiate
        ' a security token from KeyInfo clauses.
        ' </summary>
        Public Sub New(ByVal serviceToken As SecurityToken)
            MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)

            ' The token we issue will expire after 8 hours.
            _lifeTime = New LifeTime(DateTime.Now, 8 * 60 * 60)

            ' Now generate a key.
            _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
            _key.GenerateKey()

            ' Generate the encrypted form of the key.
            _encryptedKey = New EncryptedKey(serviceToken, _key.KeyBytes)

            ' Set the RawData property to the UTF8Encoding of the Token. 
            Dim tokenString As String = GetXmlString()
            RawData = (New UTF8Encoding).GetBytes(tokenString)
        End Sub

        ' This constructor is called by WSE when a SOAP message is received that contains
        ' a Binary Security Token with a ValueType element equal the ValueType property for this
        ' class. The ValueType property is Server-side constructor. It invokes the base class, which 
        ' then loads all properties from the supplied XmlElement
        ' using LoadXml(XmlElement).
        Public Sub New(ByVal element As XmlElement)
            MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)

            ' Load the Token from the XmlElement.
            LoadXml(element)
        End Sub

        ' <summary>
        ' Not used.
        ' </summary>
        Public Property AppliesTo() As AppliesTo Implements IIssuedToken.AppliesTo
            Get
                Return Nothing
            End Get
            Set(ByVal Value As AppliesTo)

            End Set
        End Property

        ' <summary>
        ' Not used.
        ' </summary>
        Public Property TokenIssuer() As EndpointReference Implements IIssuedToken.TokenIssuer
            Get
                Return Nothing
            End Get
            Set(ByVal Value As EndpointReference)

            End Set
        End Property

        ' <summary>
        ' Not used.
        ' </summary>
        Public Property BaseToken() As SecurityToken Implements IIssuedToken.BaseToken
            Get
                Return Nothing
            End Get
            Set(ByVal Value As SecurityToken)

            End Set
        End Property
        ' <summary>
        ' Return true if the token has not expired and its
        ' creation time is not a postdated time.
        ' </summary>
        Public Overrides ReadOnly Property IsCurrent() As Boolean
            Get
                Return (LifeTime Is Nothing OrElse _
                    LifeTime.IsCurrent)
            End Get
        End Property

        ' <summary>
        ' Get/Set a life time for this binary token, including creation
        ' time and expiration time.
        ' </summary>
        Public Property LifeTime() As LifeTime Implements IIssuedToken.LifeTime
            Get
                Return _lifeTime
            End Get
            Set(ByVal Value As LifeTime)
                _lifeTime = Value
            End Set
        End Property

        ' <summary>
        ' Get/Set the proof token for this binary token. This is the 
        ' encrypted form of the key for the token requestor.
        ' </summary>
        Public Property ProofToken() As RequestedProofToken Implements IIssuedToken.ProofToken
            Get
                Return _proofToken
            End Get
            Set(ByVal Value As RequestedProofToken)
                _proofToken = Value
            End Set
        End Property


        ' <summary>
        ' Not used.
        ' </summary>
        Public ReadOnly Property SupportingTokens() As SecurityTokenCollection Implements IIssuedToken.SupportingTokens
            Get
                Return Nothing
            End Get
        End Property

        ' <summary>
        ' Return true if two tokens have the same raw data.
        ' </summary>
        Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
            If (token Is Nothing OrElse _
                Not token.GetType() Is Me.GetType()) Then
                Return False
            Else
                Dim t As BinaryToken = CType(token, BinaryToken)
                If (Not RawData Is Nothing AndAlso _
                    Not t.RawData Is Nothing AndAlso _
                    RawData.Length = t.RawData.Length) Then
                    Dim index As Integer = RawData.Length
                    While (--index > -1)
                        If (RawData(index) <> t.RawData(index)) Then
                            Return False
                        End If
                    End While
                    Return True
                ElseIf (RawData Is Nothing AndAlso _
                    t.RawData Is Nothing) Then
                    Return True
                Else
                    Return False
                End If
            End If
        End Function

        ' <summary>
        ' Return the hash of the raw data for this token.
        ' </summary>
        Public Overrides Function GetHashCode() As Integer
            If (Not RawData Is Nothing) Then
                Return RawData.GetHashCode()
            Else
                Return 0
            End If
        End Function
        ' <summary>
        ' Load an instance of this class through an XmlElement.
        ' </summary>
        ' <param name="element">The XmlElement version of the token</param>
        Public Overrides Sub LoadXml(ByVal element As XmlElement)
            ' The default implementation parses the required elements for the
            ' <BinarySecurityToken> element and assigns them to their respective 
            ' properties in the BinarySecurityToken class. These properties are
            ' RawData, ValueType, and EncodingType.
            MyBase.LoadXml(element)

            ' Reset the existing contents.
            _lifeTime = Nothing
            _key = Nothing
            _encryptedKey = Nothing
            _proofToken = Nothing

            ' Check to see if any contents in the RawData element
            ' were assigned to the RawData property by the base class LoadXml
            ' method. If RawData is not null, then the sender's public and private
            ' key pair is contained within it.
            If (Not RawData Is Nothing) Then
                ' The sender's key is stored in the RawData
                Dim tokenString As String = (New UTF8Encoding).GetString(RawData)
                Dim document As XmlDocument = New XmlDocument
                document.LoadXml(tokenString)
                Dim tokenElement As XmlElement = document.DocumentElement

                Dim createdAt As DateTime = DateTime.MinValue
                Dim expiresAt As DateTime = DateTime.MaxValue

                Select Case tokenElement.LocalName
                    Case BinaryTokenNames.TokenName
                        Dim child As XmlNode
                        For Each child In tokenElement.ChildNodes
                            Select Case child.LocalName
                                Case BinaryTokenNames.CreatedAt
                                    createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)

                                Case BinaryTokenNames.ExpiresAt
                                    expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)

                                Case BinaryTokenNames.BinaryKey
                                    _encryptedKey = New EncryptedKey(CType(child.FirstChild, XmlElement))
                            End Select
                        Next
                End Select

                If (createdAt <> DateTime.MinValue OrElse _
                    expiresAt <> DateTime.MaxValue) Then
                    _lifeTime = New LifeTime(createdAt, expiresAt)
                End If
            End If
        End Sub

        Public Function GetXmlString() As String
            Dim document As XmlDocument = New XmlDocument
            Dim element As XmlElement = document.CreateElement(BinaryTokenNames.TokenName)

            '
            ' LifeTime
            '
            If (Not _lifeTime Is Nothing AndAlso _
                _lifeTime.Created <> DateTime.MinValue) Then
                Dim createdAt As XmlElement = document.CreateElement(BinaryTokenNames.CreatedAt)
                createdAt.InnerText = XmlConvert.ToString( _
                    _lifeTime.Created.ToUniversalTime(), _
                    WSUtility.TimeFormat)
                element.AppendChild(createdAt)
            End If

            '
            ' ExpiresAt
            '
            If (Not _lifeTime Is Nothing AndAlso _
                _lifeTime.Expires <> DateTime.MaxValue) Then
                Dim expiresAt As XmlElement = document.CreateElement(BinaryTokenNames.ExpiresAt)
                expiresAt.InnerText = XmlConvert.ToString( _
                    _lifeTime.Expires.ToUniversalTime(), _
                    WSUtility.TimeFormat)
                element.AppendChild(expiresAt)
            End If

            ' 
            ' Binary Key. The binary key is carried within the token as an EncryptedKey. 
            '
            If (Not _encryptedKey Is Nothing) Then
                Dim binaryKey As XmlElement = document.CreateElement(BinaryTokenNames.BinaryKey)
                binaryKey.AppendChild(_encryptedKey.GetXml(document))
                element.AppendChild(binaryKey)
            End If

            Return element.OuterXml
        End Function

        ' <summary>
        ' Returns the key for the security token.
        ' </summary>
        Public Overrides ReadOnly Property Key() As KeyAlgorithm
            Get
                If (_key Is Nothing) Then
                    If (Not _proofToken Is Nothing) Then
                        '
                        ' Attempt recovery from the proof token.
                        '
                        _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                        _key.KeyBytes = _proofToken.KeyBytes
                    ElseIf (Not _encryptedKey Is Nothing) Then
                        '
                        ' Attempt key recovery from the encrypted form.
                        '
                        _encryptedKey.Decrypt()

                        _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                        _key.KeyBytes = _encryptedKey.KeyBytes
                    End If
                End If

                Return _key
            End Get
        End Property

        ' <summary>
        ' This token supports XML digital signatures.
        ' </summary>
        Public Overrides ReadOnly Property SupportsDigitalSignature() As Boolean
            Get
                Return True
            End Get
        End Property

        ' <summary>
        ' This token supports data encryption.
        ' </summary>
        Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean
            Get
                Return True
            End Get
        End Property
    End Class
End Namespace
using System;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Globalization;
using System.Xml;
using System.Configuration;

using Microsoft.Web.Services3.Addressing ;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Cryptography;
using Microsoft.Web.Services3.Security.Utility;
using Microsoft.Web.Services3.Security.Tokens;

namespace CustomBinaryTokenCode
{
    [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] 
    public class BinaryToken : BinarySecurityToken, IIssuedToken
   {
        // Life time of this token.
        private LifeTime              _lifeTime     = null;
        // The encrypted form of the key for the target service.
        private EncryptedKey          _encryptedKey = null;
        // The encrypted form of the key for the token requestor.
        private RequestedProofToken   _proofToken   = null;
        // The key for the token. This will be an AES128 key.
        private SymmetricKeyAlgorithm _key          = null;

        /// <summary>
        /// The security token manager uses this constructor to instantiate
        /// a security token from KeyInfo clauses.
        /// </summary>
        public BinaryToken(SecurityToken serviceToken) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) 
        {
            // The token we issue will expire after 8 hours.
            _lifeTime = new LifeTime(DateTime.Now, 8 * 60 * 60 );

            // Now generate a key.
            _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
            _key.GenerateKey();
    
            // Generate the encrypted form of the key.
            _encryptedKey = new EncryptedKey(serviceToken,_key.KeyBytes);

            // Set the RawData property to the UTF8Encoding of the Token. 
            string tokenString = GetXmlString();
            RawData = (new UTF8Encoding()).GetBytes(tokenString);
        }
        // This constructor is called by WSE when a SOAP message is received that contains
        // a Binary Security Token with a ValueType element equal the ValueType property for this
        // class. The ValueType property is Server-side constructor. It invokes the base class, which 
        // then loads all properties from the supplied XmlElement
        // using LoadXml(XmlElement).
        public BinaryToken(XmlElement element) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
        {
            // Load the Token from the XmlElement.
            LoadXml(element);
        }
        /// <summary>
        /// Not used.
        /// </summary>
        public AppliesTo AppliesTo
        {
            get
            {
                return null;
            }
            set
            {
            }
        } 
        /// <summary>
        /// Not used.
        /// </summary>
        public EndpointReference  TokenIssuer
        {
            get
            {
                return null;
            }
            set
            {

            }
        }

        /// <summary>
        /// Not used.
        /// </summary>
        public SecurityToken BaseToken
        {
            get
            {
                return null;
            }
            set
            {
            }
        }

        /// <summary>
        /// Return true if the token has not expired and its
        /// creation time is not a postdated time.
        /// </summary>
        public override bool IsCurrent
        {
            get
            {
                return (LifeTime == null || LifeTime.IsCurrent);
            }
        }

        /// <summary>
        /// Get/Set a life time for this binary token, including creation
        /// time and expiration time.
        /// </summary>
        public LifeTime LifeTime
        {
            get
            {
                return _lifeTime;
            }
            set
            {
                _lifeTime = value;
            }
        }

        /// <summary>
        /// Get/Set the proof token for this binary token. This is the 
        /// encrypted form of the key for the token requestor.
        /// </summary>
        public RequestedProofToken ProofToken
        {
            get
            {
                return _proofToken;
            }
            set
            {
                _proofToken = value;
            }
        }
        /// <summary>
        /// Not used.
        /// </summary>
        public SecurityTokenCollection SupportingTokens
        {
            get
            {
                return null;
            }
            set
            {
            }
        }

        /// <summary>
        /// Return true if two tokens have the same raw data.
        /// </summary>
        public override bool Equals(SecurityToken token)
        {
            if ( token == null || token.GetType() != GetType() )
                return false;
            else
            {
                BinaryToken t = (BinaryToken)token;
                if (RawData != null && t.RawData != null &&
                    RawData.Length == t.RawData.Length)
                {
                    int index = RawData.Length;
                    while (--index > -1)
                        if (RawData[index] != t.RawData[index])
                            return false;
                    return true;
                }
                else if (RawData == null && t.RawData == null)
                    return true;
                else
                    return false;
            }
        }

        /// <summary>
        /// Return the hash of the raw data for this token.
        /// </summary>
        public override int GetHashCode()
        {
            if ( RawData != null )
                return RawData.GetHashCode();
            else
                return 0;
        }

        /// <summary>
        /// Load an instance of this class through an XmlElement.
        /// </summary>
        /// <param name="element">The XmlElement version of the token</param>
        public override void LoadXml(XmlElement element)
        {
            // The default implementation parses the required elements for the
            // <BinarySecurityToken> element and assigns them to their respective 
            // properties in the BinarySecurityToken class. These properties are
            // RawData, ValueType, and EncodingType.
            base.LoadXml(element);

            // Reset the existing contents.
            _lifeTime       = null;
            _key            = null;
            _encryptedKey   = null;
            _proofToken     = null;

            // Check to see if any contents in the RawData element
            // were assigned to the RawData property by the base class LoadXml
            // method. If RawData is not null, then the sender's public and private
            // key pair is contained within it.
            if (RawData != null)
            {
                // The sender's key is stored in the RawData
                string tokenString = (new UTF8Encoding()).GetString(RawData);
                XmlDocument document = new XmlDocument();
                document.LoadXml(tokenString);
                XmlElement tokenElement = document.DocumentElement;

                DateTime createdAt = DateTime.MinValue;
                DateTime expiresAt = DateTime.MaxValue;

                switch ( tokenElement.LocalName ) 
                {
                    case BinaryTokenNames.TokenName:
                    {
                        foreach(XmlNode child in tokenElement.ChildNodes ) 
                        {
                            switch ( child.LocalName ) 
                            {
                                case BinaryTokenNames.CreatedAt:
                                    createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
                                    break;

                                case BinaryTokenNames.ExpiresAt:
                                    expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);                                
                                    break;

                                case BinaryTokenNames.BinaryKey:
                                    _encryptedKey = new EncryptedKey(child.FirstChild as XmlElement);
                                    break;

                                default:
                                    break;                                    
                            }
                        }
                        break;
                    }
                }

                if ( createdAt != DateTime.MinValue || expiresAt != DateTime.MaxValue )
                    _lifeTime = new LifeTime(createdAt, expiresAt);
            }
        }

        public string GetXmlString()
        {
            XmlDocument document = new XmlDocument();
            XmlElement  element = document.CreateElement(BinaryTokenNames.TokenName);

            //
            // LifeTime
            //
            if (_lifeTime != null && _lifeTime.Created != DateTime.MinValue)
            {
                XmlElement createdAt = document.CreateElement(BinaryTokenNames.CreatedAt);
                createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat);
                element.AppendChild(createdAt);
            }

            //
            // ExpiresAt
            //
            if (_lifeTime != null && _lifeTime.Expires != DateTime.MaxValue)
            {
                XmlElement expiresAt = document.CreateElement(BinaryTokenNames.ExpiresAt);
                expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat);
                element.AppendChild(expiresAt);
            }

            // 
            // Binary Key. The binary key is carried within the token as an EncryptedKey. 
            //
            if (_encryptedKey != null )
            {
                XmlElement binaryKey = document.CreateElement(BinaryTokenNames.BinaryKey);
                binaryKey.AppendChild(_encryptedKey.GetXml(document));
                element.AppendChild(binaryKey);
            }

            return element.OuterXml;
        }

        /// <summary>
        /// Returns the key for the security token.
        /// </summary>
        public override KeyAlgorithm Key
        {
            get
            {
                if ( _key == null )
                {
                    if ( _proofToken != null )
                    {
                        //
                        // Attempt recovery from the proof token.
                        //
                        _key          = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                        _key.KeyBytes = _proofToken.KeyBytes;
                    }
                    else if ( _encryptedKey != null )
                    {
                        //
                        // Attempt key recovery from the encrypted form.
                        //
                        _encryptedKey.Decrypt();

                        _key          = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                        _key.KeyBytes = _encryptedKey.KeyBytes;
                    }
                }

                return _key;
            }
        }


        /// <summary>
        /// This token supports XML digital signatures.
        /// </summary>
        public override bool SupportsDigitalSignature
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// This token supports data encryption.
        /// </summary>
        public override bool SupportsDataEncryption
        {
            get
            {
                return true;
            }
        }
    }
}

See Also

Tasks

How to: Create a Security Token Manager for a Custom Security Token

Reference

BinarySecurityToken Class

Other Resources

Creating Custom Security Tokens