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, 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 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. Create a new Class Library project in Visual Studio .NET 2003.

  2. 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.Services2.dll, System.Web.Services.dll, System.Security.dll, and System.Xml.dll, and then click Select.
    3. Click OK.
  3. 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.Xml
    Imports System.Text
    Imports System.Security.Permissions
    Imports System.Security.Cryptography
    Imports System.Security.Cryptography.Xml
    Imports System.Globalization
    Imports System.Configuration
    
    Imports Microsoft.Web.Services2.Policy
    Imports Microsoft.Web.Services2.Security
    Imports Microsoft.Web.Services2.Security.Cryptography
    Imports Microsoft.Web.Services2.Security.Tokens
    Imports Microsoft.Web.Services2.Security.Utility
    
    using System;
    using System.Xml;
    using System.Text;
    using System.Security.Permissions;
    using System.Security.Cryptography;
    using System.Security.Cryptography.Xml;
    using System.Globalization;
    using System.Configuration;
    
    using Microsoft.Web.Services2.Policy;
    using Microsoft.Web.Services2.Security;
    using Microsoft.Web.Services2.Security.Cryptography;
    using Microsoft.Web.Services2.Security.Tokens;
    using Microsoft.Web.Services2.Security.Utility;
    
  4. Add a class that derives from the BinarySecurityToken class.

    Public Class BinaryToken
        Inherits BinarySecurityToken
    
    public class BinaryToken : BinarySecurityToken
    

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
    

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.

    ' The first section of code declares member variables for the class.
    ' life time of this token
    ' 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 'New 
    
    //  The first section of code declares member variables for the class.
    // 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 the 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 'New 
    
    // This constructor is called by WSE when a SOAP message is received that contains
    // a Binary Security Token with a ValueType element equal to 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.

                  ' <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 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(child.FirstChild) '
    
    
                                          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 If
                  End Sub 'LoadXml
                  Public Function GetXmlString() As String
                      Dim document As 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 'GetXmlString
    
    
                  /// <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;
                  }
    
    
  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
    
    Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean
        Get
            Return True
        End Get
    End Property
    
    public override bool SupportsDigitalSignature
    {
        get
        {
            return true;
        }
    }
    
    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 an AES key of 128 bits in length.

                  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.

    Public Overrides ReadOnly Property IsCurrent() As Boolean
        Get
            Return True
        End Get
    End Property
    Public Property LifeTime() As LifeTime Implements IIssuedToken.LifeTime
        Get
            Return _lifeTime
        End Get
        Set(ByVal Value As LifeTime)
            _lifeTime = Value
        End Set
    End Property
    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 override bool IsCurrent
    {
        get
        {
            return true;
        }
    }
    public LifeTime LifeTime
    {
        get
        {
            return _lifeTime;
        }
        set
        {
            _lifeTime = value;
        }
    }
    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.

    Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
        If token Is Nothing OrElse Not TypeOf token Is BinaryToken Then
            Return False
        Else
            Dim t As BinaryToken = CType(token, BinaryToken)
            Return AppBase.CompareArray(RawData, t.RawData)
        End If
    End Function 'Equals
    
    Public Overrides Function GetHashCode() As Integer
        Return RawData.GetHashCode()
    End Function 'GetHashCode
    
    public override bool Equals(SecurityToken token)
    {
        if ( token == null || token.GetType() != GetType() )
            return false;
        else
        {
            BinaryToken t = (BinaryToken)token;
            return AppBase.CompareArray(RawData, t.RawData);
        }
    }
    
    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.

Namespace CustomBinaryTokenCode
<SecurityPermission(SecurityAction.Demand, Flags := SecurityPermissionFlag.UnmanagedCode)> _
Public Class BinaryToken
    Inherits BinarySecurityToken
    Implements IIssuedToken 'ToDo: Add Implements Clauses for implementation methods of these interface(s)

' 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 'New


' This constructor is called by the WSE when a SOAP message is received containing
' a Binary Security Token with a ValueType element equal to 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 '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>
' 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>
' 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 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 raw data.
' </summary>
Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
    If token Is Nothing OrElse Not TypeOf token Is BinaryToken Then
        Return False
    Else
        Dim t As BinaryToken = CType(token, BinaryToken)
        Return AppBase.CompareArray(RawData, t.RawData)
    End If
End Function 'Equals


' <summary>
' Return the hash of the raw data for this token.
' </summary>
Public Overrides Function GetHashCode() As Integer
    Return RawData.GetHashCode()
End Function 'GetHashCode


' <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 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 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(child.FirstChild) '
                            
                        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 If
End Sub 'LoadXml

Public Function GetXmlString() As String
    Dim document As 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 'GetXmlString

' <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>
' 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>
' 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 'BinaryToken
End Namespace 'CustomBinaryTokenCode
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 Uri TokenIssuer
{
    get
    {
        return null;
    }
    set
    {
    }
}

/// <summary>
/// Not used.
/// </summary>
public SecurityToken BaseToken
{
    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 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 raw data.
/// </summary>
public override bool Equals(SecurityToken token)
{
    if ( token == null || token.GetType() != GetType() )
        return false;
    else
    {
        BinaryToken t = (BinaryToken)token;
        return AppBase.CompareArray(RawData, t.RawData);
    }
}

/// <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>
/// 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>
/// 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