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:
- Create a class that derives from the SecurityToken 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 SecurityToken class
Create a new Class Library project in Visual Studio .NET 2003.
Add references to the Micrsosoft.Web.Services2, 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 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;
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
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; } }
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
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); }
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; } }
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; } }
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); }
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