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:
- Create a class that derives from the BinarySecurityToken class.
- Apply an appropriate security permission attribute.
- Implement constructors with parameters that reflect your token's data.
- Override the appropriate methods and properties of your class.
To create a class that derives from the BinarySecurityToken class
Create a new Class Library project in Visual Studio .NET 2003.
Add references to the Micrsosoft.Web.Services, System.Web.Services, System.Security, and System.Xml assemblies.
- In Solution Explorer, right-click References, and then click Add Reference.
- 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.
- Click OK.
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;
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
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); }
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
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; }
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; } }
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; } }
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; } }
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