How to: Create a Class Representing a Custom Binary Security Token
The first step to using custom binary security credentials within a SOAP message is to create a class that represents the custom binary security token, as shown in this topic. After that class is created, create another class that represents a security token manager must be created. For more information about creating a security token manager, see How to: Create a Security Token Manager for a Custom Security Token.
WSE 3.0 allows a security token service to change the type of security token that is being issued without having to update clients when the security token is an opaque token. Previous versions of WSE required the client to be updated to accommodate a different type of issued security token. Specifically, WSE 2.0 required a client application to register all security token managers for issued security tokens; with WSE 3.0, that is no longer necessary. For more details about creating an opaque security token, see SAML STS.
The following procedures create a class that represents a custom binary security token:
- 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
The following procedure creates a custom binary security token for the AES algorithm. To create a custom binary security token for your custom credentials, replace the code in the class with code that is specific to your custom credentials.
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.Services3.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.Security.Cryptography Imports System.Security.Permissions Imports System.Text Imports System.Globalization Imports System.Xml Imports System.Configuration Imports Microsoft.Web.Services3.Addressing Imports Microsoft.Web.Services3.Design Imports Microsoft.Web.Services3.Security Imports Microsoft.Web.Services3.Security.Cryptography Imports Microsoft.Web.Services3.Security.Utility Imports Microsoft.Web.Services3.Security.Tokens
using System; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Globalization; using System.Xml; using System.Configuration; using Microsoft.Web.Services3.Addressing ; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; using Microsoft.Web.Services3.Security.Cryptography; using Microsoft.Web.Services3.Security.Utility; using Microsoft.Web.Services3.Security.Tokens;
Add a class that derives from the BinarySecurityToken class.
<SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _ Public Class BinaryToken Inherits BinarySecurityToken
public class BinaryToken : BinarySecurityToken, IIssuedToken
To apply an appropriate security permission attribute
Apply the SecurityPermissionAttribute attribute to your class to help prevent an arbitrary assembly from accessing your token.
It is recommended that you demand that an assembly accessing this class already have permission to call unmanaged code. This helps prevent partially trusted code from gaining access to the cryptographic key information that the security token provides.
The following code example applies the SecurityPermissionAttribute attribute to the class derived from the BinarySecurityToken class, demanding the UnmanagedCode permission.
<SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _ Public Class BinaryToken Inherits BinarySecurityToken
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] public class BinaryToken : BinarySecurityToken, IIssuedToken
To implement constructors with parameters that reflect your token's data
Add one or more constructors that take the data necessary to represent your custom security credentials.
This constructor is called when a message is received that includes data encrypted with the token. After this constructor runs, the token's Key property is able to decrypt the data.
' Life time of this token. Private _lifeTime As LifeTime = Nothing ' The encrypted form of the key for the target service. Private _encryptedKey As EncryptedKey = Nothing ' The encrypted form of the key for the token requestor. Private _proofToken As RequestedProofToken = Nothing ' The key for the token. This will be an AES128 key. Private _key As SymmetricKeyAlgorithm = Nothing ' <summary> ' The security token manager uses this constructor to instantiate ' a security token from KeyInfo clauses. ' </summary> Public Sub New(ByVal serviceToken As SecurityToken) MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) ' The token we issue will expire after 8 hours. _lifeTime = New LifeTime(DateTime.Now, 8 * 60 * 60) ' Now generate a key. _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm) _key.GenerateKey() ' Generate the encrypted form of the key. _encryptedKey = New EncryptedKey(serviceToken, _key.KeyBytes) ' Set the RawData property to the UTF8Encoding of the Token. Dim tokenString As String = GetXmlString() RawData = (New UTF8Encoding).GetBytes(tokenString) End Sub
// Life time of this token. private LifeTime _lifeTime = null; // The encrypted form of the key for the target service. private EncryptedKey _encryptedKey = null; // The encrypted form of the key for the token requestor. private RequestedProofToken _proofToken = null; // The key for the token. This will be an AES128 key. private SymmetricKeyAlgorithm _key = null; /// <summary> /// The security token manager uses this constructor to instantiate /// a security token from KeyInfo clauses. /// </summary> public BinaryToken(SecurityToken serviceToken) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) { // The token we issue will expire after 8 hours. _lifeTime = new LifeTime(DateTime.Now, 8 * 60 * 60 ); // Now generate a key. _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128"); _key.GenerateKey(); // Generate the encrypted form of the key. _encryptedKey = new EncryptedKey(serviceToken,_key.KeyBytes); // Set the RawData property to the UTF8Encoding of the Token. string tokenString = GetXmlString(); RawData = (new UTF8Encoding()).GetBytes(tokenString); }
Implement a constructor that takes an XmlElement argument.
WSE calls this constructor when a SOAP message is received that contains your custom security binary token in the WS-Security SOAP header. The XmlElement argument is the <BinarySecurityToken> element within a WS-Security SOAP header from a received message. This constructor has a default implementation in the BinarySecurityToken class that loads known XML, such as the value of the RawData property.
The following code calls the base class constructor, which calls the LoadXml method that populates the RawData property if it exists in the XML.
' This constructor is called by WSE when a SOAP message is received that contains ' a Binary Security Token with a ValueType element equal the ValueType property for this ' class. The ValueType property is Server-side constructor. It invokes the base class, which ' then loads all properties from the supplied XmlElement ' using LoadXml(XmlElement). Public Sub New(ByVal element As XmlElement) MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) ' Load the Token from the XmlElement. LoadXml(element) End Sub
// This constructor is called by WSE when a SOAP message is received that contains // a Binary Security Token with a ValueType element equal the ValueType property for this // class. The ValueType property is Server-side constructor. It invokes the base class, which // then loads all properties from the supplied XmlElement // using LoadXml(XmlElement). public BinaryToken(XmlElement element) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType) { // Load the Token from the XmlElement. LoadXml(element); }
To override the appropriate methods and properties of your class
Override the LoadXml and GetXml methods.
The LoadXml method is called by the default implementation of the constructor that accepts an XmlElement argument. The default implementation of the LoadXml method can parse the contents of the <BinarySecurityToken> element into the RawData, ValueType, and EncodingType properties. The default implementation does not, however, know what is stored in the RawData element; so, if a custom binary security token adds unique data to the RawData element, override the LoadXml method to parse out that unique data.
The GetXml method is called by WSE when the custom binary security token is serialized into the SOAP message. For most cases, the default implementation is sufficient.
The following code example retrieves the sender's public key from the RawData element. The following code example does not override the GetXml method, but rather creates a GetXmlString method, so that a developer for a sender can retrieve an XML string of the sender's public key.
Public Overrides Sub LoadXml(ByVal element As XmlElement) ' The default implementation parses the required elements for the ' <BinarySecurityToken> element and assigns them to their respective ' properties in the BinarySecurityToken class. These properties are ' RawData, ValueType, and EncodingType. MyBase.LoadXml(element) ' Reset the existing contents. _lifeTime = Nothing _key = Nothing _encryptedKey = Nothing _proofToken = Nothing ' Check to see if any contents in the RawData element ' were assigned to the RawData property by the base class LoadXml ' method. If RawData is not null, then the sender's public and private ' key pair is contained within it. If (Not RawData Is Nothing) Then ' The sender's key is stored in the RawData Dim tokenString As String = (New UTF8Encoding).GetString(RawData) Dim document As XmlDocument = New XmlDocument document.LoadXml(tokenString) Dim tokenElement As XmlElement = document.DocumentElement Dim createdAt As DateTime = DateTime.MinValue Dim expiresAt As DateTime = DateTime.MaxValue Select Case tokenElement.LocalName Case BinaryTokenNames.TokenName Dim child As XmlNode For Each child In tokenElement.ChildNodes Select Case child.LocalName Case BinaryTokenNames.CreatedAt createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture) Case BinaryTokenNames.ExpiresAt expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture) Case BinaryTokenNames.BinaryKey _encryptedKey = New EncryptedKey(CType(child.FirstChild, XmlElement)) End Select Next End Select If (createdAt <> DateTime.MinValue OrElse _ expiresAt <> DateTime.MaxValue) Then _lifeTime = New LifeTime(createdAt, expiresAt) End If End If End Sub Public Function GetXmlString() As String Dim document As XmlDocument = New XmlDocument Dim element As XmlElement = document.CreateElement(BinaryTokenNames.TokenName) ' ' LifeTime ' If (Not _lifeTime Is Nothing AndAlso _ _lifeTime.Created <> DateTime.MinValue) Then Dim createdAt As XmlElement = document.CreateElement(BinaryTokenNames.CreatedAt) createdAt.InnerText = XmlConvert.ToString( _ _lifeTime.Created.ToUniversalTime(), _ WSUtility.TimeFormat) element.AppendChild(createdAt) End If ' ' ExpiresAt ' If (Not _lifeTime Is Nothing AndAlso _ _lifeTime.Expires <> DateTime.MaxValue) Then Dim expiresAt As XmlElement = document.CreateElement(BinaryTokenNames.ExpiresAt) expiresAt.InnerText = XmlConvert.ToString( _ _lifeTime.Expires.ToUniversalTime(), _ WSUtility.TimeFormat) element.AppendChild(expiresAt) End If ' ' Binary Key. The binary key is carried within the token as an EncryptedKey. ' If (Not _encryptedKey Is Nothing) Then Dim binaryKey As XmlElement = document.CreateElement(BinaryTokenNames.BinaryKey) binaryKey.AppendChild(_encryptedKey.GetXml(document)) element.AppendChild(binaryKey) End If Return element.OuterXml End Function
public override void LoadXml(XmlElement element) { // The default implementation parses the required elements for the // <BinarySecurityToken> element and assigns them to their respective // properties in the BinarySecurityToken class. These properties are // RawData, ValueType, and EncodingType. base.LoadXml(element); // Reset the existing contents. _lifeTime = null; _key = null; _encryptedKey = null; _proofToken = null; // Check to see if any contents in the RawData element // were assigned to the RawData property by the base class LoadXml // method. If RawData is not null, then the sender's public and private // key pair is contained within it. if (RawData != null) { // The sender's key is stored in the RawData string tokenString = (new UTF8Encoding()).GetString(RawData); XmlDocument document = new XmlDocument(); document.LoadXml(tokenString); XmlElement tokenElement = document.DocumentElement; DateTime createdAt = DateTime.MinValue; DateTime expiresAt = DateTime.MaxValue; switch ( tokenElement.LocalName ) { case BinaryTokenNames.TokenName: { foreach(XmlNode child in tokenElement.ChildNodes ) { switch ( child.LocalName ) { case BinaryTokenNames.CreatedAt: createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture); break; case BinaryTokenNames.ExpiresAt: expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture); break; case BinaryTokenNames.BinaryKey: _encryptedKey = new EncryptedKey(child.FirstChild as XmlElement); break; default: break; } } break; } } if ( createdAt != DateTime.MinValue || expiresAt != DateTime.MaxValue ) _lifeTime = new LifeTime(createdAt, expiresAt); } } public string GetXmlString() { XmlDocument document = new XmlDocument(); XmlElement element = document.CreateElement(BinaryTokenNames.TokenName); // // LifeTime // if (_lifeTime != null && _lifeTime.Created != DateTime.MinValue) { XmlElement createdAt = document.CreateElement(BinaryTokenNames.CreatedAt); createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat); element.AppendChild(createdAt); } // // ExpiresAt // if (_lifeTime != null && _lifeTime.Expires != DateTime.MaxValue) { XmlElement expiresAt = document.CreateElement(BinaryTokenNames.ExpiresAt); expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat); element.AppendChild(expiresAt); } // // Binary Key. The binary key is carried within the token as an EncryptedKey. // if (_encryptedKey != null ) { XmlElement binaryKey = document.CreateElement(BinaryTokenNames.BinaryKey); binaryKey.AppendChild(_encryptedKey.GetXml(document)); element.AppendChild(binaryKey); } return element.OuterXml; }
Override the SupportsDigitalSignature and SupportsDataEncryption properties.
These Boolean properties indicate whether the custom binary security can be used for digital signatures and data encryption.
The following code example specifies that the custom binary security token can be used for all these purposes.
Public Overrides ReadOnly Property SupportsDigitalSignature() As Boolean Get Return True End Get End Property ' <summary> ' This token supports data encryption. ' </summary> Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean Get Return True End Get End Property
public override bool SupportsDigitalSignature { get { return true; } } /// <summary> /// This token supports data encryption. /// </summary> public override bool SupportsDataEncryption { get { return true; } }
Override the Key property.
The Key property is accessed by WSE to specify the key that is used to perform signing and encryption operations on a SOAP message. This property is of type KeyAlgorithm and is either a SymmetricKeyAlgorithm or an AsymmetricKeyAlgorithm.
The following code example returns a SymmetricAlgorithm for a 128-bit AES key.
Public Overrides ReadOnly Property Key() As KeyAlgorithm Get If (_key Is Nothing) Then If (Not _proofToken Is Nothing) Then ' ' Attempt recovery from the proof token. ' _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm) _key.KeyBytes = _proofToken.KeyBytes ElseIf (Not _encryptedKey Is Nothing) Then ' ' Attempt key recovery from the encrypted form. ' _encryptedKey.Decrypt() _key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm) _key.KeyBytes = _encryptedKey.KeyBytes End If End If Return _key End Get End Property
public override KeyAlgorithm Key { get { if ( _key == null ) { if ( _proofToken != null ) { // // Attempt recovery from the proof token. // _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128"); _key.KeyBytes = _proofToken.KeyBytes; } else if ( _encryptedKey != null ) { // // Attempt key recovery from the encrypted form. // _encryptedKey.Decrypt(); _key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128"); _key.KeyBytes = _encryptedKey.KeyBytes; } } return _key; } }
Override the IsCurrent, RequestedProofToken, and LifeTime properties.
The following code example specifies that the security token is valid.
' <summary> ' Return true if the token has not expired and its ' creation time is not a postdated time. ' </summary> Public Overrides ReadOnly Property IsCurrent() As Boolean Get Return (LifeTime Is Nothing OrElse _ LifeTime.IsCurrent) End Get End Property ' <summary> ' Get/Set a life time for this binary token, including creation ' time and expiration time. ' </summary> Public Property LifeTime() As LifeTime Implements IIssuedToken.LifeTime Get Return _lifeTime End Get Set(ByVal Value As LifeTime) _lifeTime = Value End Set End Property ' <summary> ' Get/Set the proof token for this binary token. This is the ' encrypted form of the key for the token requestor. ' </summary> Public Property ProofToken() As RequestedProofToken Implements IIssuedToken.ProofToken Get Return _proofToken End Get Set(ByVal Value As RequestedProofToken) _proofToken = Value End Set End Property
/// <summary> /// Return true if the token has not expired and its /// creation time is not a postdated time. /// </summary> public override bool IsCurrent { get { return (LifeTime == null || LifeTime.IsCurrent); } } /// <summary> /// Get/Set a life time for this binary token, including creation /// time and expiration time. /// </summary> public LifeTime LifeTime { get { return _lifeTime; } set { _lifeTime = value; } } /// <summary> /// Get/Set the proof token for this binary token. This is the /// encrypted form of the key for the token requestor. /// </summary> public RequestedProofToken ProofToken { get { return _proofToken; } set { _proofToken = value; } }
Override the GetHashCode and Equals methods.
The GetHashCode method serves as a hash function for the custom binary security token when the type is added to a Hashtable collection. Override the Equals method so that the Hashtable collection works correctly. The Equals method guarantees that two objects that are considered equal have the same hash code. For more information about these two methods, see Object.GetHashCode.
The following code example creates a hash code based on the security token's key.
' <summary> ' Return true if two tokens have the same raw data. ' </summary> Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean If (token Is Nothing OrElse _ Not token.GetType() Is Me.GetType()) Then Return False Else Dim t As BinaryToken = CType(token, BinaryToken) If (Not RawData Is Nothing AndAlso _ Not t.RawData Is Nothing AndAlso _ RawData.Length = t.RawData.Length) Then Dim index As Integer = RawData.Length While (--index > -1) If (RawData(index) <> t.RawData(index)) Then Return False End If End While Return True ElseIf (RawData Is Nothing AndAlso _ t.RawData Is Nothing) Then Return True Else Return False End If End If End Function ' <summary> ' Return the hash of the raw data for this token. ' </summary> Public Overrides Function GetHashCode() As Integer If (Not RawData Is Nothing) Then Return RawData.GetHashCode() Else Return 0 End If End Function
/// <summary> /// Return true if two tokens have the same raw data. /// </summary> public override bool Equals(SecurityToken token) { if ( token == null || token.GetType() != GetType() ) return false; else { BinaryToken t = (BinaryToken)token; if (RawData != null && t.RawData != null && RawData.Length == t.RawData.Length) { int index = RawData.Length; while (--index > -1) if (RawData[index] != t.RawData[index]) return false; return true; } else if (RawData == null && t.RawData == null) return true; else return false; } } /// <summary> /// Return the hash of the raw data for this token. /// </summary> public override int GetHashCode() { if ( RawData != null ) return RawData.GetHashCode(); else return 0; }
Example
The following code example is a complete implementation of a custom binary token.
Imports System
Imports System.Security.Cryptography
Imports System.Security.Permissions
Imports System.Text
Imports System.Globalization
Imports System.Xml
Imports System.Configuration
Imports Microsoft.Web.Services3.Addressing
Imports Microsoft.Web.Services3.Design
Imports Microsoft.Web.Services3.Security
Imports Microsoft.Web.Services3.Security.Cryptography
Imports Microsoft.Web.Services3.Security.Utility
Imports Microsoft.Web.Services3.Security.Tokens
Namespace CustomBinaryTokenCode
<SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)> _
Public Class BinaryToken
Inherits BinarySecurityToken
Implements IIssuedToken
' Life time of this token.
Private _lifeTime As LifeTime = Nothing
' The encrypted form of the key for the target service.
Private _encryptedKey As EncryptedKey = Nothing
' The encrypted form of the key for the token requestor.
Private _proofToken As RequestedProofToken = Nothing
' The key for the token. This will be an AES128 key.
Private _key As SymmetricKeyAlgorithm = Nothing
' <summary>
' The security token manager uses this constructor to instantiate
' a security token from KeyInfo clauses.
' </summary>
Public Sub New(ByVal serviceToken As SecurityToken)
MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
' The token we issue will expire after 8 hours.
_lifeTime = New LifeTime(DateTime.Now, 8 * 60 * 60)
' Now generate a key.
_key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
_key.GenerateKey()
' Generate the encrypted form of the key.
_encryptedKey = New EncryptedKey(serviceToken, _key.KeyBytes)
' Set the RawData property to the UTF8Encoding of the Token.
Dim tokenString As String = GetXmlString()
RawData = (New UTF8Encoding).GetBytes(tokenString)
End Sub
' This constructor is called by WSE when a SOAP message is received that contains
' a Binary Security Token with a ValueType element equal the ValueType property for this
' class. The ValueType property is Server-side constructor. It invokes the base class, which
' then loads all properties from the supplied XmlElement
' using LoadXml(XmlElement).
Public Sub New(ByVal element As XmlElement)
MyBase.New(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
' Load the Token from the XmlElement.
LoadXml(element)
End Sub
' <summary>
' Not used.
' </summary>
Public Property AppliesTo() As AppliesTo Implements IIssuedToken.AppliesTo
Get
Return Nothing
End Get
Set(ByVal Value As AppliesTo)
End Set
End Property
' <summary>
' Not used.
' </summary>
Public Property TokenIssuer() As EndpointReference Implements IIssuedToken.TokenIssuer
Get
Return Nothing
End Get
Set(ByVal Value As EndpointReference)
End Set
End Property
' <summary>
' Not used.
' </summary>
Public Property BaseToken() As SecurityToken Implements IIssuedToken.BaseToken
Get
Return Nothing
End Get
Set(ByVal Value As SecurityToken)
End Set
End Property
' <summary>
' Return true if the token has not expired and its
' creation time is not a postdated time.
' </summary>
Public Overrides ReadOnly Property IsCurrent() As Boolean
Get
Return (LifeTime Is Nothing OrElse _
LifeTime.IsCurrent)
End Get
End Property
' <summary>
' Get/Set a life time for this binary token, including creation
' time and expiration time.
' </summary>
Public Property LifeTime() As LifeTime Implements IIssuedToken.LifeTime
Get
Return _lifeTime
End Get
Set(ByVal Value As LifeTime)
_lifeTime = Value
End Set
End Property
' <summary>
' Get/Set the proof token for this binary token. This is the
' encrypted form of the key for the token requestor.
' </summary>
Public Property ProofToken() As RequestedProofToken Implements IIssuedToken.ProofToken
Get
Return _proofToken
End Get
Set(ByVal Value As RequestedProofToken)
_proofToken = Value
End Set
End Property
' <summary>
' Not used.
' </summary>
Public ReadOnly Property SupportingTokens() As SecurityTokenCollection Implements IIssuedToken.SupportingTokens
Get
Return Nothing
End Get
End Property
' <summary>
' Return true if two tokens have the same raw data.
' </summary>
Public Overloads Overrides Function Equals(ByVal token As SecurityToken) As Boolean
If (token Is Nothing OrElse _
Not token.GetType() Is Me.GetType()) Then
Return False
Else
Dim t As BinaryToken = CType(token, BinaryToken)
If (Not RawData Is Nothing AndAlso _
Not t.RawData Is Nothing AndAlso _
RawData.Length = t.RawData.Length) Then
Dim index As Integer = RawData.Length
While (--index > -1)
If (RawData(index) <> t.RawData(index)) Then
Return False
End If
End While
Return True
ElseIf (RawData Is Nothing AndAlso _
t.RawData Is Nothing) Then
Return True
Else
Return False
End If
End If
End Function
' <summary>
' Return the hash of the raw data for this token.
' </summary>
Public Overrides Function GetHashCode() As Integer
If (Not RawData Is Nothing) Then
Return RawData.GetHashCode()
Else
Return 0
End If
End Function
' <summary>
' Load an instance of this class through an XmlElement.
' </summary>
' <param name="element">The XmlElement version of the token</param>
Public Overrides Sub LoadXml(ByVal element As XmlElement)
' The default implementation parses the required elements for the
' <BinarySecurityToken> element and assigns them to their respective
' properties in the BinarySecurityToken class. These properties are
' RawData, ValueType, and EncodingType.
MyBase.LoadXml(element)
' Reset the existing contents.
_lifeTime = Nothing
_key = Nothing
_encryptedKey = Nothing
_proofToken = Nothing
' Check to see if any contents in the RawData element
' were assigned to the RawData property by the base class LoadXml
' method. If RawData is not null, then the sender's public and private
' key pair is contained within it.
If (Not RawData Is Nothing) Then
' The sender's key is stored in the RawData
Dim tokenString As String = (New UTF8Encoding).GetString(RawData)
Dim document As XmlDocument = New XmlDocument
document.LoadXml(tokenString)
Dim tokenElement As XmlElement = document.DocumentElement
Dim createdAt As DateTime = DateTime.MinValue
Dim expiresAt As DateTime = DateTime.MaxValue
Select Case tokenElement.LocalName
Case BinaryTokenNames.TokenName
Dim child As XmlNode
For Each child In tokenElement.ChildNodes
Select Case child.LocalName
Case BinaryTokenNames.CreatedAt
createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
Case BinaryTokenNames.ExpiresAt
expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture)
Case BinaryTokenNames.BinaryKey
_encryptedKey = New EncryptedKey(CType(child.FirstChild, XmlElement))
End Select
Next
End Select
If (createdAt <> DateTime.MinValue OrElse _
expiresAt <> DateTime.MaxValue) Then
_lifeTime = New LifeTime(createdAt, expiresAt)
End If
End If
End Sub
Public Function GetXmlString() As String
Dim document As XmlDocument = New XmlDocument
Dim element As XmlElement = document.CreateElement(BinaryTokenNames.TokenName)
'
' LifeTime
'
If (Not _lifeTime Is Nothing AndAlso _
_lifeTime.Created <> DateTime.MinValue) Then
Dim createdAt As XmlElement = document.CreateElement(BinaryTokenNames.CreatedAt)
createdAt.InnerText = XmlConvert.ToString( _
_lifeTime.Created.ToUniversalTime(), _
WSUtility.TimeFormat)
element.AppendChild(createdAt)
End If
'
' ExpiresAt
'
If (Not _lifeTime Is Nothing AndAlso _
_lifeTime.Expires <> DateTime.MaxValue) Then
Dim expiresAt As XmlElement = document.CreateElement(BinaryTokenNames.ExpiresAt)
expiresAt.InnerText = XmlConvert.ToString( _
_lifeTime.Expires.ToUniversalTime(), _
WSUtility.TimeFormat)
element.AppendChild(expiresAt)
End If
'
' Binary Key. The binary key is carried within the token as an EncryptedKey.
'
If (Not _encryptedKey Is Nothing) Then
Dim binaryKey As XmlElement = document.CreateElement(BinaryTokenNames.BinaryKey)
binaryKey.AppendChild(_encryptedKey.GetXml(document))
element.AppendChild(binaryKey)
End If
Return element.OuterXml
End Function
' <summary>
' Returns the key for the security token.
' </summary>
Public Overrides ReadOnly Property Key() As KeyAlgorithm
Get
If (_key Is Nothing) Then
If (Not _proofToken Is Nothing) Then
'
' Attempt recovery from the proof token.
'
_key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
_key.KeyBytes = _proofToken.KeyBytes
ElseIf (Not _encryptedKey Is Nothing) Then
'
' Attempt key recovery from the encrypted form.
'
_encryptedKey.Decrypt()
_key = CType(KeyAlgorithm.Create("AES128"), SymmetricKeyAlgorithm)
_key.KeyBytes = _encryptedKey.KeyBytes
End If
End If
Return _key
End Get
End Property
' <summary>
' This token supports XML digital signatures.
' </summary>
Public Overrides ReadOnly Property SupportsDigitalSignature() As Boolean
Get
Return True
End Get
End Property
' <summary>
' This token supports data encryption.
' </summary>
Public Overrides ReadOnly Property SupportsDataEncryption() As Boolean
Get
Return True
End Get
End Property
End Class
End Namespace
using System;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Globalization;
using System.Xml;
using System.Configuration;
using Microsoft.Web.Services3.Addressing ;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Cryptography;
using Microsoft.Web.Services3.Security.Utility;
using Microsoft.Web.Services3.Security.Tokens;
namespace CustomBinaryTokenCode
{
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
public class BinaryToken : BinarySecurityToken, IIssuedToken
{
// Life time of this token.
private LifeTime _lifeTime = null;
// The encrypted form of the key for the target service.
private EncryptedKey _encryptedKey = null;
// The encrypted form of the key for the token requestor.
private RequestedProofToken _proofToken = null;
// The key for the token. This will be an AES128 key.
private SymmetricKeyAlgorithm _key = null;
/// <summary>
/// The security token manager uses this constructor to instantiate
/// a security token from KeyInfo clauses.
/// </summary>
public BinaryToken(SecurityToken serviceToken) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
{
// The token we issue will expire after 8 hours.
_lifeTime = new LifeTime(DateTime.Now, 8 * 60 * 60 );
// Now generate a key.
_key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
_key.GenerateKey();
// Generate the encrypted form of the key.
_encryptedKey = new EncryptedKey(serviceToken,_key.KeyBytes);
// Set the RawData property to the UTF8Encoding of the Token.
string tokenString = GetXmlString();
RawData = (new UTF8Encoding()).GetBytes(tokenString);
}
// This constructor is called by WSE when a SOAP message is received that contains
// a Binary Security Token with a ValueType element equal the ValueType property for this
// class. The ValueType property is Server-side constructor. It invokes the base class, which
// then loads all properties from the supplied XmlElement
// using LoadXml(XmlElement).
public BinaryToken(XmlElement element) : base(BinaryTokenNames.ValueType, BinaryTokenNames.TokenType)
{
// Load the Token from the XmlElement.
LoadXml(element);
}
/// <summary>
/// Not used.
/// </summary>
public AppliesTo AppliesTo
{
get
{
return null;
}
set
{
}
}
/// <summary>
/// Not used.
/// </summary>
public EndpointReference TokenIssuer
{
get
{
return null;
}
set
{
}
}
/// <summary>
/// Not used.
/// </summary>
public SecurityToken BaseToken
{
get
{
return null;
}
set
{
}
}
/// <summary>
/// Return true if the token has not expired and its
/// creation time is not a postdated time.
/// </summary>
public override bool IsCurrent
{
get
{
return (LifeTime == null || LifeTime.IsCurrent);
}
}
/// <summary>
/// Get/Set a life time for this binary token, including creation
/// time and expiration time.
/// </summary>
public LifeTime LifeTime
{
get
{
return _lifeTime;
}
set
{
_lifeTime = value;
}
}
/// <summary>
/// Get/Set the proof token for this binary token. This is the
/// encrypted form of the key for the token requestor.
/// </summary>
public RequestedProofToken ProofToken
{
get
{
return _proofToken;
}
set
{
_proofToken = value;
}
}
/// <summary>
/// Not used.
/// </summary>
public SecurityTokenCollection SupportingTokens
{
get
{
return null;
}
set
{
}
}
/// <summary>
/// Return true if two tokens have the same raw data.
/// </summary>
public override bool Equals(SecurityToken token)
{
if ( token == null || token.GetType() != GetType() )
return false;
else
{
BinaryToken t = (BinaryToken)token;
if (RawData != null && t.RawData != null &&
RawData.Length == t.RawData.Length)
{
int index = RawData.Length;
while (--index > -1)
if (RawData[index] != t.RawData[index])
return false;
return true;
}
else if (RawData == null && t.RawData == null)
return true;
else
return false;
}
}
/// <summary>
/// Return the hash of the raw data for this token.
/// </summary>
public override int GetHashCode()
{
if ( RawData != null )
return RawData.GetHashCode();
else
return 0;
}
/// <summary>
/// Load an instance of this class through an XmlElement.
/// </summary>
/// <param name="element">The XmlElement version of the token</param>
public override void LoadXml(XmlElement element)
{
// The default implementation parses the required elements for the
// <BinarySecurityToken> element and assigns them to their respective
// properties in the BinarySecurityToken class. These properties are
// RawData, ValueType, and EncodingType.
base.LoadXml(element);
// Reset the existing contents.
_lifeTime = null;
_key = null;
_encryptedKey = null;
_proofToken = null;
// Check to see if any contents in the RawData element
// were assigned to the RawData property by the base class LoadXml
// method. If RawData is not null, then the sender's public and private
// key pair is contained within it.
if (RawData != null)
{
// The sender's key is stored in the RawData
string tokenString = (new UTF8Encoding()).GetString(RawData);
XmlDocument document = new XmlDocument();
document.LoadXml(tokenString);
XmlElement tokenElement = document.DocumentElement;
DateTime createdAt = DateTime.MinValue;
DateTime expiresAt = DateTime.MaxValue;
switch ( tokenElement.LocalName )
{
case BinaryTokenNames.TokenName:
{
foreach(XmlNode child in tokenElement.ChildNodes )
{
switch ( child.LocalName )
{
case BinaryTokenNames.CreatedAt:
createdAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
break;
case BinaryTokenNames.ExpiresAt:
expiresAt = Convert.ToDateTime(child.InnerXml, CultureInfo.InvariantCulture);
break;
case BinaryTokenNames.BinaryKey:
_encryptedKey = new EncryptedKey(child.FirstChild as XmlElement);
break;
default:
break;
}
}
break;
}
}
if ( createdAt != DateTime.MinValue || expiresAt != DateTime.MaxValue )
_lifeTime = new LifeTime(createdAt, expiresAt);
}
}
public string GetXmlString()
{
XmlDocument document = new XmlDocument();
XmlElement element = document.CreateElement(BinaryTokenNames.TokenName);
//
// LifeTime
//
if (_lifeTime != null && _lifeTime.Created != DateTime.MinValue)
{
XmlElement createdAt = document.CreateElement(BinaryTokenNames.CreatedAt);
createdAt.InnerText = XmlConvert.ToString(_lifeTime.Created.ToUniversalTime(), WSUtility.TimeFormat);
element.AppendChild(createdAt);
}
//
// ExpiresAt
//
if (_lifeTime != null && _lifeTime.Expires != DateTime.MaxValue)
{
XmlElement expiresAt = document.CreateElement(BinaryTokenNames.ExpiresAt);
expiresAt.InnerText = XmlConvert.ToString(_lifeTime.Expires.ToUniversalTime(), WSUtility.TimeFormat);
element.AppendChild(expiresAt);
}
//
// Binary Key. The binary key is carried within the token as an EncryptedKey.
//
if (_encryptedKey != null )
{
XmlElement binaryKey = document.CreateElement(BinaryTokenNames.BinaryKey);
binaryKey.AppendChild(_encryptedKey.GetXml(document));
element.AppendChild(binaryKey);
}
return element.OuterXml;
}
/// <summary>
/// Returns the key for the security token.
/// </summary>
public override KeyAlgorithm Key
{
get
{
if ( _key == null )
{
if ( _proofToken != null )
{
//
// Attempt recovery from the proof token.
//
_key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
_key.KeyBytes = _proofToken.KeyBytes;
}
else if ( _encryptedKey != null )
{
//
// Attempt key recovery from the encrypted form.
//
_encryptedKey.Decrypt();
_key = (SymmetricKeyAlgorithm)KeyAlgorithm.Create("AES128");
_key.KeyBytes = _encryptedKey.KeyBytes;
}
}
return _key;
}
}
/// <summary>
/// This token supports XML digital signatures.
/// </summary>
public override bool SupportsDigitalSignature
{
get
{
return true;
}
}
/// <summary>
/// This token supports data encryption.
/// </summary>
public override bool SupportsDataEncryption
{
get
{
return true;
}
}
}
}
See Also
Tasks
How to: Create a Security Token Manager for a Custom Security Token