Share via


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

The first step to using custom XML security credentials within a SOAP message is to create a class that represents the custom XML security token. This topic details how to do that. After that class is created, 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.

This procedure consists of the following steps:

  1. Create a class that derives from the SecurityToken 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 SecurityToken class

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

  2. Add references to the Micrsosoft.Web.Services2, 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.Services2.dll, System.Web.Services.dll, System.Security.dll, and System.Xml.dll, and then click Select.
    3. Click OK.
  3. Add the Imports statements or using directives shown in the following code example to the top of the file for Class1.cs.

    Imports System
    Imports System.Security.Permissions
    Imports System.Security.Cryptography
    Imports System.Security.Cryptography.Xml
    Imports System.Xml
    Imports Microsoft.Web.Services2.Security
    Imports Microsoft.Web.Services2.Security.Tokens
    
    using System;
    using System.Security.Permissions;
    using System.Security.Cryptography;
    using System.Security.Cryptography.Xml;
    using System.Xml;
    using Microsoft.Web.Services2.Security;
    using Microsoft.Web.Services2.Security.Tokens;
    
  4. Add a class that derives from the SecurityToken class.

    If the custom security token will be issued using a security token service, also implement the IIssuedToken interface.

    Public Class XmlToken
        Inherits SecurityToken
        Implements IIssuedToken
    
    public class XmlToken : SecurityToken, 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 SecurityToken class, demanding the UnmanagedCode permission.

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

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.

    When an encrypted SOAP message is received by WSE, WSE calls the security token manager to retrieve the security token needed to decrypt the SOAP message. The security token manager then calls this constructor to create an instance of the security token.The following code example implements a constructor and properties for the token's lifetime and requ proof token.

    ' <summary>
    ' The security token manager utilizes this constructor to instantiate
    ' a security token from KeyInfo clauses.
    ' </summary>
    Public Sub New(ByVal serviceToken As SecurityToken)
            MyBase.New(XmlTokenNames.TypeNames.TokenType)
            ' Set the lift time
            _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
                _serviceToken = serviceToken
     End Sub 'New
    
    ' The encrypted form of the key for the token.
    Private _keysXml As XmlElement = Nothing
    ' The encrypted form of the key supplied as a proof token
    Private _proofToken As RequestedProofToken = Nothing
    ' _serviceToken is used to encrypt the key for the target service
    Private _serviceToken As SecurityToken = Nothing
    ' The key for the token. This will be an AES128 key.
    ' We use AES128 as our Symmetric Algorithm.
    Private _key As SymmetricKeyAlgorithm = Nothing
    ' life time of this token
    Private _lifeTime As LifeTime = Nothing
    ' For any other extensible element
    Private _signedXml As XmlElement = Nothing
    
    ' <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
    
    public XmlToken(SecurityToken serviceToken) : base(XmlTokenNames.TypeNames.TokenType) 
    {
         // Set the lift time.
         _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.
         _serviceToken = serviceToken;
       }
    // private variables used by the token
    // The encrypted form of the key for the token.
    private XmlElement              _keysXml        = null;
    // The encrypted form of the key supplied as a proof token
    private RequestedProofToken     _proofToken     = null;
    // _serviceToken is used to encrypt the key for the target service
    private SecurityToken           _serviceToken   = null;  
    // The key for the token. This will be an AES128 key.
    // We use AES128 as our Symmetric Algorithm.
    private SymmetricKeyAlgorithm   _key            = null;
    // life time of this token
    private LifeTime                _lifeTime       = null;
    // For any other extensible element
    private XmlElement              _signedXml      = null;
    
    /// <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;
        }
    }
    
  2. Implement a constructor that takes an XmlElement argument.

    When a SOAP message is received that contains an element in the WS-Security header matching the qname attribute of the <securityTokenManager> Element configuration element, WSE calls the LoadTokenFromXml method of the security token manager. The LoadTokenFromXml method can then call this constructor to deserialize the XML into your custom XML security token.

    Public Sub New(element As XmlElement)
      MyBase.New(TokenValueType)
      LoadXml(element)
    End Sub
    
    public XmlToken(XmlElement element) : base(XmlTokenNames.TypeNames.TokenType)
    {
         LoadXml(element);            
    }
    

To override the appropriate methods and properties of your class

  1. Override the LoadXml and GetXml methods.

    The LoadXml method initializes the properties of the security token by parsing the XmlElement argument passed to it. The constructor that accepts an XmlElement argument can call the LoadXml method to parse the XmlElement argument passed to it.

    The GetXml method is called by WSE when the custom XML security token is serialized into the SOAP message.

    The following code example demonstrates how to populate a custom XML security token from an XmlElement by using the LoadXml method and how to serialize a custom XML security token by using the GetXml method.

    Public Overrides Function GetXml(ByVal document As XmlDocument) As XmlElement
         If document Is Nothing Then
                   Throw New ArgumentNullException("document")
         End If
         Dim element As XmlElement = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.Token, XmlTokenNames.Namespace)
    
         '
         ' The id attribute
         '
         If Not (Id Is Nothing) AndAlso Id.Length <> 0 Then
              Dim attr As XmlAttribute = document.CreateAttribute(WSUtility.Prefix, WSUtility.AttributeNames.Id, WSUtility.NamespaceURI)
              attr.InnerText = Id
                   element.Attributes.Append(attr)
         End If
    
         '
         ' CreatedAt
         '
         If Not (_lifeTime Is Nothing) AndAlso _lifeTime.Created <> DateTime.MinValue Then
              Dim createdAt As XmlElement = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.CreatedAt, XmlTokenNames.Namespace)
              createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat)
              element.AppendChild(createdAt)
         End If
    
         '
         ' ExpiresAt
         '
         If Not (_lifeTime Is Nothing) AndAlso _lifeTime.Expires <> DateTime.MinValue Then
              Dim expiresAt As XmlElement = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.ExpiresAt, XmlTokenNames.Namespace)
              expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat)
              element.AppendChild(expiresAt)
         End If
    
         '
         ' _keys: use _keysXml if we don't understand it
         '
         If Not (_keysXml Is Nothing) Then
              element.AppendChild(document.ImportNode(_keysXml, True))
         ElseIf Not (_serviceToken Is Nothing) Then
              element.AppendChild(New EncryptedKey(_serviceToken, _key.KeyBytes).GetXml(document))
         End If
    
         '
         ' envelope signature
         '
         If Not _signedXml Is Nothing Then
              element.AppendChild(document.ImportNode(_signedXml, True))
         End If
    
         Return element
    End Function 'GetXml
    
    Public Overrides Sub LoadXml(ByVal element As XmlElement)
         If element Is Nothing Then
              Throw New ArgumentNullException("element")
         End If
    
         ' Reset existing contents
         _lifeTime = Nothing
         _key = Nothing
         _keysXml = Nothing
         _proofToken = Nothing
    
         Dim createdAt As DateTime = DateTime.MinValue
         Dim expiresAt As DateTime = DateTime.MaxValue
    
         Select Case element.LocalName
              Case XmlTokenNames.ElementNames.Token
              ' get the Id out of the token
              Id = element.GetAttribute(WSUtility.AttributeNames.Id, WSUtility.NamespaceURI)
    
         Dim child As XmlNode
         For Each child In element.ChildNodes
              Select Case child.LocalName
                   Case XmlTokenNames.ElementNames.CreatedAt
                        createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
                   Case XmlTokenNames.ElementNames.ExpiresAt
                        expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
                   Case XmlEncryption.ElementNames.EncryptedKey
                        _keysXml = New XmlDocument().ImportNode(child, True)
                   Case XmlSignature.ElementNames.Signature
                        _signedXml = New XmlDocument().ImportNode(child, True)
                   Case Else
              End Select
         Next child
          End Select
    
         If createdAt <> DateTime.MinValue OrElse expiresAt <> DateTime.MaxValue Then
                _lifeTime = New LifeTime(createdAt, expiresAt)
          End If
    End Sub 
    
    public override XmlElement GetXml(XmlDocument document) 
    {
         if (document == null)
              throw new ArgumentNullException("document");
    
         XmlElement element = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.Token, XmlTokenNames.Namespace);
    
         //
         // The id attribute
         //
         if ( Id != null && Id.Length != 0 )
         {
              XmlAttribute attr = document.CreateAttribute(WSUtility.Prefix, WSUtility.AttributeNames.Id, WSUtility.NamespaceURI);
              attr.InnerText = Id;
              element.Attributes.Append(attr);
         }
    
         //
         // CreatedAt
         //
         if (_lifeTime != null && _lifeTime.Created != DateTime.MinValue)
         {
              XmlElement createdAt = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.CreatedAt, XmlTokenNames.Namespace);
              createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat);
              element.AppendChild(createdAt);
         }
    
         //
         // ExpiresAt
         //
         if (_lifeTime != null && _lifeTime.Expires != DateTime.MinValue)
         {
              XmlElement expiresAt = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.ExpiresAt, XmlTokenNames.Namespace );
              expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat);
              element.AppendChild(expiresAt);
         }
    
         //
         // _keys: use _keysXml if we don't understand it
         //
         if (_keysXml != null )
                  element.AppendChild(document.ImportNode(_keysXml,true));
         else if ( _serviceToken != null )
              element.AppendChild(new EncryptedKey(_serviceToken, _key.KeyBytes).GetXml(document));
    
          //
          // envelope signature
          //
          if ( _signedXml != null )
                    element.AppendChild(document.ImportNode(_signedXml,true));
    
          return element;
    }
    
    public override void LoadXml(XmlElement element) 
    {
         if ( null == element )
              throw new ArgumentNullException("element");
    
         // Reset existing contents
         _lifeTime       = null;
         _key            = null;
         _keysXml        = null;
         _proofToken     = null;
    
         DateTime createdAt = DateTime.MinValue;
         DateTime expiresAt = DateTime.MaxValue;
    
         switch ( element.LocalName ) 
         {
              case XmlTokenNames.ElementNames.Token:
                   // get the Id out of the token
                   Id = element.GetAttribute(WSUtility.AttributeNames.Id, WSUtility.NamespaceURI);
    
                   foreach(XmlNode child in element.ChildNodes ) 
                   {
                            switch ( child.LocalName ) 
                            {
                                case XmlTokenNames.ElementNames.CreatedAt:
                                    createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
                                    break;
                                case XmlTokenNames.ElementNames.ExpiresAt:
                                    expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);                                
                                    break;
                                case XmlEncryption.ElementNames.EncryptedKey:
                                    _keysXml = new XmlDocument().ImportNode(child, true) as XmlElement;
                                    break;
                                case XmlSignature.ElementNames.Signature:
                                    _signedXml = new XmlDocument().ImportNode(child, true) as XmlElement;
                                    break;
                                default:
                                    break;                                    
                            }
                   }
                   break;
              }
    
              if ( createdAt != DateTime.MinValue || expiresAt != DateTime.MaxValue )
                   _lifeTime = new LifeTime(createdAt, expiresAt);
    }
    
  2. Override the SupportsDigitalSignature and SupportsDataEncryption properties.

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

    The following code example specifies that the custom XML security token can be used for both digitally signing and encrypting SOAP messages.

    ' This class supports XML digital signatures.
    Public Overrides ReadOnly Property SupportsDigitalSignature() As _
        Boolean
        Get
            Return True
        End Get
    End Property
    
    ' This class supports encryption.
    Public Overrides ReadOnly Property SupportsDataEncryption() As _
        Boolean
        Get
            Return True
        End Get 
    End Property
    
    public override bool SupportsDataEncryption
    {
         get
         {
              return true;
         }
    }
    
    public override bool SupportsDigitalSignature
    {
         get
         {
              return true;
         }
    }  
    
  3. Override the Key property.

    The Key property is accessed by WSE to specify the key that is used to perform cryptographic operations on a SOAP message, including signing and encryption.

    The following code example returns a key for the based on an AES key of 128 bits.

                  ' <summary>
                  ' Returns the key for the security token.
                  ' </summary>
                  Public Overrides ReadOnly Property Key() As KeyAlgorithm
                      Get
                          If Not (_proofToken Is Nothing) Then
                              '
                              ' Attempt recovery from the proof token.
                              '    
                              _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                              _key.KeyBytes = _proofToken.KeyBytes
                          ElseIf Not (_keysXml Is Nothing) Then
                              '
                              ' Attempt key recovery from the encrypted form.
                              '
                              Dim encryptedKey As New encryptedKey(_keysXml)
                              encryptedKey.Decrypt()
                              _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
                              _key.KeyBytes = encryptedKey.KeyBytes
                          End If
                          Return _key
                      End Get
                  End Property
    
    
    /// <summary>
            /// Returns the key for the security token.
            /// </summary>
            public override KeyAlgorithm Key
            {
                get
                {
                    if ( _proofToken != null )
                    {
                        //
                        // Attempt recovery from the proof token.
                        //    
                        _key          = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                        _key.KeyBytes = _proofToken.KeyBytes;
                    }
                    else if ( _keysXml != null )
                    {
                        //
                        // Attempt key recovery from the encrypted form.
                        //
                        EncryptedKey encryptedKey = new EncryptedKey(_keysXml);
    
                        encryptedKey.Decrypt();
    
                        _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
                        _key.KeyBytes = encryptedKey.KeyBytes;
                    }
    
                    return _key;
                }
    }
    
  4. Override the GetHashCode and Equals methods.

    The GetHashCode method serves as a hash function for the custom XML 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, which are considered equal, have the same hash code. Equality for this example is based on the key bytes. For more information about these two methods, see Object.GetHashCode.

    The following code example creates a hash code based on the security token's expiration date and time, key, and the name of the key container that contains the key.

    ' <summary>
    ' Return the hash of the key material for this token.
    ' </summary>
    Public Overrides Function GetHashCode() As Integer
        Dim h As Integer = 0
    
        If Not (_key Is Nothing) Then
            h += _key.KeyBytes.GetHashCode()
        End If
        Return h
    End Function
    
    ' <summary>
    ' Return true if two tokens has the same key material.
    ' </summary>
    Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
        If token Is Nothing OrElse Not TypeOf token Is xmlToken Then
            Return False
        End If
        Dim xmlToken As xmlToken = CType(token, xmlToken)
        Return AppBase.CompareArray(_key.KeyBytes, xmlToken._key.KeyBytes)
    End Function 'Equals
    
    /// <summary>
    /// Return the hash of the key material for this token.
    /// </summary>
    public override int GetHashCode()
    {
        int h = 0;
    
        if (_key != null)
            h += _key.KeyBytes.GetHashCode();
    
        return h;
    }
    
    /// <summary>
    /// Return true if two tokens have the same key material.
    /// </summary>
    public override bool Equals(SecurityToken token)
    {
        if (token == null || !(token is XmlToken))
            return false;
    
        XmlToken xmlToken = (XmlToken)token;
        return AppBase.CompareArray(_key.KeyBytes, xmlToken._key.KeyBytes);
    }
    
  5. Override the IsCurrent method.

    The IsCurrent method determines whether the security token is valid at the current moment.

    The following code example verifies that the security token has not expired.

    ' <summary>
    ' Determines if the current token is valid for security operations.
    ' </summary>
    Public Overrides ReadOnly Property IsCurrent() As Boolean
        Get
            Return _lifeTime Is Nothing OrElse _lifeTime.IsCurrent
        End Get
    End Property
    
    /// <summary>
    /// Determines if the current token is valid for security operations.
    /// </summary>
    public override bool IsCurrent
    {
        get
        {
            return (_lifeTime == null || _lifeTime.IsCurrent);
        }
    }
    

Example

The following code example demonstrates how to build a class representing a custom XML security token.

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

Imports Microsoft.Web.Services2.Policy
Imports Microsoft.Web.Services2.Security
Imports Microsoft.Web.Services2.Security.Cryptography
Imports Microsoft.Web.Services2.Security.Utility
Imports Microsoft.Web.Services2.Security.Tokens

Imports Microsoft.Web.Services2.QuickStart



Namespace CustomXmlSecTokenCode
<SecurityPermission(SecurityAction.Demand, Flags := SecurityPermissionFlag.UnmanagedCode)>  _
Public Class XmlToken
Inherits SecurityToken
Implements IIssuedToken 'ToDo: Add Implements Clauses for implementation methods of these interface(s)
' The encrypted form of the key for the token.
Private _keysXml As XmlElement = Nothing
' The encrypted form of the key supplied as a proof token.
Private _proofToken As RequestedProofToken = Nothing
' _serviceToken is used to encrypt the key for the target service. Private _serviceToken As SecurityToken = Nothing
' The key for the token. This will be an AES128 key.
' We use AES128 as our Symmetric Algorithm.
Private _key As SymmetricKeyAlgorithm = Nothing
' life time of this token
Private _lifeTime As LifeTime = Nothing
' For any other extensible element
Private _signedXml As XmlElement = 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(XmlTokenNames.TypeNames.TokenType)
    ' Set the lift time.
    _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.
    _serviceToken = serviceToken
End Sub 'New


' <summary>
' This constructor deserializes an XmlToken from its xml representation.
' </summary>
' <param name="element"></param>
Public Sub New(ByVal element As XmlElement)
    MyBase.New(XmlTokenNames.TypeNames.TokenType)
    LoadXml(element)
End Sub 'New

' <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 Uri Implements IIssuedToken.TokenIssuer
    Get
        Return Nothing
    End Get
    Set(ByVal Value As Uri)
    End Set
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>
' 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>
' Not used
' </summary>

Public Property SupportingTokens() As SecurityTokenCollection Implements IIssuedToken.SupportingTokens
    Get
        Return Nothing
    End Get
    Set(ByVal Value As SecurityTokenCollection)
    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 two tokens has the same key material
' </summary>
Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
    If token Is Nothing OrElse Not TypeOf token Is xmlToken Then
        Return False
    End If
    Dim xmlToken As xmlToken = CType(token, xmlToken)
    Return AppBase.CompareArray(_key.KeyBytes, xmlToken._key.KeyBytes)
End Function 'Equals


' <summary>
' Return hash of the key material for this token
' </summary>
Public Overrides Function GetHashCode() As Integer
    Dim h As Integer = 0

    If Not (_key Is Nothing) Then
        h += _key.KeyBytes.GetHashCode()
    End If
    Return h
End Function 'GetHashCode

' <summary>
' Returns the key for the security token.
' </summary>

Public Overrides ReadOnly Property Key() As KeyAlgorithm
    Get
        If Not (_proofToken Is Nothing) Then
            '
            ' Attempt recovery from the proof token.
            '    
            _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
            _key.KeyBytes = _proofToken.KeyBytes
        ElseIf Not (_keysXml Is Nothing) Then
            '
            ' Attempt key recovery from the encrypted form.
            '
            Dim encryptedKey As New encryptedKey(_keysXml)

            encryptedKey.Decrypt()

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

        Return _key
    End Get
End Property


' <summary>
'  Serialize this class to a Xmlelement.
' </summary>
' <param name="document">the newly generated Xmlelment belongs to this Xmldocument</param>
' <returns></returns>
Public Overrides Function GetXml(ByVal document As XmlDocument) As XmlElement
    If document Is Nothing Then
        Throw New ArgumentNullException("document")
    End If
    Dim element As XmlElement = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.Token, XmlTokenNames.Namespace)

    '
    ' The id attribute
    '
    If Not (Id Is Nothing) AndAlso Id.Length <> 0 Then
        Dim attr As XmlAttribute = document.CreateAttribute(WSUtility.Prefix, WSUtility.AttributeNames.Id, WSUtility.NamespaceURI)
        attr.InnerText = Id
        element.Attributes.Append(attr)
    End If

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

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

    '
    ' _keys: use _keysXml if we don't understand it
    '
    If Not (_keysXml Is Nothing) Then
        element.AppendChild(document.ImportNode(_keysXml, True))
    ElseIf Not (_serviceToken Is Nothing) Then
        element.AppendChild(New EncryptedKey(_serviceToken, _key.KeyBytes).GetXml(document))
    End If

    '
    ' envelope signature
    '
    If Not _signedXml Is Nothing Then
        element.AppendChild(document.ImportNode(_signedXml, True))
    End If

    Return element
End Function 'GetXml


' <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)
    If element Is Nothing Then
        Throw New ArgumentNullException("element")
    End If
    ' Reset existing contents
    _lifeTime = Nothing
    _key = Nothing
    _keysXml = Nothing
    _proofToken = Nothing

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

    Select Case element.LocalName
        Case XmlTokenNames.ElementNames.Token
            ' get the Id out of the token
            Id = element.GetAttribute(WSUtility.AttributeNames.Id, WSUtility.NamespaceURI)

            Dim child As XmlNode
            For Each child In element.ChildNodes
                Select Case child.LocalName
                    Case XmlTokenNames.ElementNames.CreatedAt
                        createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
                    Case XmlTokenNames.ElementNames.ExpiresAt
                        expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
                    Case XmlEncryption.ElementNames.EncryptedKey
                        _keysXml = New XmlDocument().ImportNode(child, True)
                    Case XmlSignature.ElementNames.Signature
                        _signedXml = New XmlDocument().ImportNode(child, True)
                    Case Else
                End Select
            Next child
    End Select

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

' <summary>
' Determines if the current token is valid for security operations.
' </summary>

Public Overrides ReadOnly Property IsCurrent() As Boolean
    Get
        Return _lifeTime Is Nothing OrElse _lifeTime.IsCurrent
    End Get
End Property

' <summary>
' Returns true if the token supports Data Encryption.
' </summary>
Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean
    Get
        Return True
    End Get
End Property

' <summary>
' Returns true if the token supports Digital Signature.
' </summary>
Public Overrides ReadOnly Property SupportsDigitalSignature() As Boolean
     Get
          Return True
     End Get
End Property
End Class 'XmlToken
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Permissions;
using System.Text;
using System.Globalization;
using System.Xml;
using System.Configuration;

using Microsoft.Web.Services2.Policy;
using Microsoft.Web.Services2.Security;
using Microsoft.Web.Services2.Security.Cryptography;
using Microsoft.Web.Services2.Security.Utility;
using Microsoft.Web.Services2.Security.Tokens;

using Microsoft.Web.Services2.QuickStart;


namespace CustomXmlSecTokenCode
{
[SecurityPermission(SecurityAction.Demand, Flags= SecurityPermissionFlag.UnmanagedCode)] 
public class XmlToken : SecurityToken, IIssuedToken
{
// The encrypted form of the key for the token.
private XmlElement              _keysXml        = null;
// The encrypted form of the key supplied as a proof token.
private RequestedProofToken     _proofToken     = null;
// _serviceToken is used to encrypt the key for the target service.
private SecurityToken           _serviceToken   = null;  
// The key for the token. This will be an AES128 key.
// We use AES128 as our Symmetric Algorithm.
private SymmetricKeyAlgorithm   _key            = null;
// life time of this token
private LifeTime                _lifeTime       = null;

// For any other extensible element
private XmlElement              _signedXml      = null;

/// <summary>
/// The security token manager uses this constructor to instantiate
/// a security token from KeyInfo clauses.
/// </summary>
public XmlToken(SecurityToken serviceToken) : base(XmlTokenNames.TypeNames.TokenType) 
{
    // Set the lift time
    _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.
    _serviceToken = serviceToken;
}

/// <summary>
/// This constructor deserializes an XmlToken from its xml representation.
/// </summary>
/// <param name="element"></param>
public XmlToken(XmlElement element) : base(XmlTokenNames.TypeNames.TokenType)
{
    LoadXml(element);            
}

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

/// <summary>
/// Not used.
/// </summary>
public Uri TokenIssuer
{
    get
    {
        return null;
    }
    set
    {
    }
}

/// <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>
/// Not used.
/// </summary>
public SecurityToken BaseToken
{
    get
    {
        return null;
    }
    set
    {
    }
}

/// <summary>
/// Not used.
/// </summary>
public SecurityTokenCollection SupportingTokens
{
    get
    {
        return null;
    }
    set
    {
    }
}

/// <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>
/// Return true if two tokens have the same key material.
/// </summary>
public override bool Equals(SecurityToken token)
{
    if (token == null || !(token is XmlToken))
        return false;

    XmlToken xmlToken = (XmlToken)token;
    return AppBase.CompareArray(_key.KeyBytes, xmlToken._key.KeyBytes);
}

/// <summary>
/// Return the hash of the key material for this token.
/// </summary>
public override int GetHashCode()
{
    int h = 0;
    
    if (_key != null)
        h += _key.KeyBytes.GetHashCode();

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

            encryptedKey.Decrypt();

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

        return _key;
    }
}

/// <summary>
///  Serialize this class to an Xmlelement
/// </summary>
/// <param name="document">the newly generated Xmlelment belongs to this Xmldocument</param>
/// <returns></returns>
public override XmlElement GetXml(XmlDocument document) 
{
    if (document == null)
        throw new ArgumentNullException("document");

    XmlElement element = document.CreateElement(XmlTokenNames.Prefix, XmlTokenNames.ElementNames.Token, XmlTokenNames.Namespace);

    //
    // The id attribute
    //
    if ( Id != null && Id.Length != 0 )
    {
        XmlAttribute attr = document.CreateAttribute(WSUtility.Prefix, WSUtility.AttributeNames.Id, WSUtility.NamespaceURI);
        attr.InnerText = Id;
        element.Attributes.Append(attr);
    }

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

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

    //
    // _keys: use _keysXml if we don't understand it
    //
    if (_keysXml != null )
        element.AppendChild(document.ImportNode(_keysXml,true));
    else if ( _serviceToken != null )
        element.AppendChild(new EncryptedKey(_serviceToken, _key.KeyBytes).GetXml(document));

    //
    // envelope signature
    //
    if ( _signedXml != null )
        element.AppendChild(document.ImportNode(_signedXml,true));

    return element;
}

/// <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) 
{
    if ( null == element )
        throw new ArgumentNullException("element");

    // Reset existing contents
    _lifeTime       = null;
    _key            = null;
    _keysXml        = null;
    _proofToken     = null;

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

    switch ( element.LocalName ) 
    {
        case XmlTokenNames.ElementNames.Token:
            // get the Id out of the token
            Id = element.GetAttribute(WSUtility.AttributeNames.Id, WSUtility.NamespaceURI);

            foreach(XmlNode child in element.ChildNodes ) 
            {
                switch ( child.LocalName ) 
                {
                    case XmlTokenNames.ElementNames.CreatedAt:
                        createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
                        break;
                    case XmlTokenNames.ElementNames.ExpiresAt:
                        expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);                                
                        break;
                    case XmlEncryption.ElementNames.EncryptedKey:
                        _keysXml = new XmlDocument().ImportNode(child, true) as XmlElement;
                        break;
                    case XmlSignature.ElementNames.Signature:
                        _signedXml = new XmlDocument().ImportNode(child, true) as XmlElement;
                        break;
                    default:
                        break;                                    
                }
            }
            break;
    }

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

/// <summary>
/// Determines if the current token is valid for security operations.
/// </summary>
public override bool IsCurrent
{
    get
    {
        return (_lifeTime == null || _lifeTime.IsCurrent);
    }
}      

        /// <summary>
        /// Returns true if the token supports Data Encryption.
        /// </summary>
        public override bool SupportsDataEncryption
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Returns true if the token supports Digital Signature.
        /// </summary>
        public override bool SupportsDigitalSignature
        {
            get
            {
                return true;
            }
        }        
    }
}

See Also

Tasks

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

Reference

SecurityToken Class

Other Resources

Creating Custom Security Tokens