//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.IdentityModel.Tokens
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IdentityModel.Configuration;
using System.IdentityModel.Diagnostics;
using System.IdentityModel.Selectors;
using System.IO;
using System.Runtime;
using System.Security.Claims;
using System.ServiceModel.Security;
using System.Xml;
using SessionDictionary = System.IdentityModel.Claims.SessionDictionary;
using SysUniqueId = System.Xml.UniqueId;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.Serialization.Formatters.Binary;
///
/// A that processes .
///
public class SessionSecurityTokenHandler : SecurityTokenHandler
{
const string DefaultCookieElementName = "Cookie";
const string DefaultCookieNamespace = "http://schemas.microsoft.com/ws/2006/05/security";
private const string SecureConversationTokenIdentifier = "http://schemas.microsoft.com/ws/2006/05/servicemodel/tokens/SecureConversation";
#pragma warning disable 1591
public static readonly TimeSpan DefaultLifetime = TimeSpan.FromHours(10);
public static readonly ReadOnlyCollection DefaultCookieTransforms = (new List(new CookieTransform[] { new DeflateCookieTransform(), new ProtectedDataCookieTransform() }).AsReadOnly());
#pragma warning restore 1591
TimeSpan _tokenLifetime = DefaultLifetime;
ReadOnlyCollection _transforms;
///
/// Initializes an instance of
///
///
/// Properties are used for defaults:
/// DefaultCookieTransforms
/// DefaultLifetime
///
public SessionSecurityTokenHandler()
: this(SessionSecurityTokenHandler.DefaultCookieTransforms)
{
}
///
/// Initializes an instance of
///
/// The transforms to apply when encoding the cookie.
///
/// Properties are used for the remaining defaults:
/// DefaultLifetime
///
public SessionSecurityTokenHandler(ReadOnlyCollection transforms)
: this(transforms, DefaultLifetime)
{ }
///
/// Initializes an instance of
///
/// The transforms to apply when encoding the cookie.
/// The default for a token.
/// Is thrown if 'transforms' is null.
/// Is thrown if 'tokenLifetime' is less than or equal to TimeSpan.Zero.
public SessionSecurityTokenHandler(ReadOnlyCollection transforms, TimeSpan tokenLifetime)
{
if (transforms == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transforms");
}
if (tokenLifetime <= TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID0016)));
}
_transforms = transforms;
_tokenLifetime = tokenLifetime;
}
///
/// Load custom configuration from Xml
///
/// XmlElement to custom configuration.
/// The param customConfigElements is null.
/// Custom configuration specified was invalid.
public override void LoadCustomConfiguration(XmlNodeList customConfigElements)
{
if (customConfigElements == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("customConfigElements");
}
List configNodes = XmlUtil.GetXmlElements(customConfigElements);
bool foundValidConfig = false;
foreach (XmlElement customConfigElement in configNodes)
{
if (!StringComparer.Ordinal.Equals(customConfigElement.LocalName, ConfigurationStrings.SessionTokenRequirement))
{
continue;
}
if (foundValidConfig)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7026, ConfigurationStrings.SessionTokenRequirement)));
}
_tokenLifetime = DefaultLifetime;
foreach (XmlAttribute attribute in customConfigElement.Attributes)
{
if (StringComparer.OrdinalIgnoreCase.Equals(attribute.LocalName, ConfigurationStrings.Lifetime))
{
TimeSpan outTokenLifetime = DefaultLifetime;
if (!TimeSpan.TryParse(attribute.Value, out outTokenLifetime))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7017, attribute.Value)));
}
if (outTokenLifetime < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7018)));
}
_tokenLifetime = outTokenLifetime;
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID7004, attribute.LocalName, customConfigElement.LocalName)));
}
}
foundValidConfig = true;
}
}
///
/// Gets the name for the cookie element.
///
public virtual string CookieElementName
{
get { return DefaultCookieElementName; }
}
///
/// Gets the namspace for the cookie element.
///
public virtual string CookieNamespace
{
get { return DefaultCookieNamespace; }
}
///
/// Applies Transforms to the cookie.
///
/// The cookie that will be transformed.
/// Controls if the cookie should be encoded (true) or decoded (false)
/// Encoded cookie.
protected virtual byte[] ApplyTransforms(byte[] cookie, bool outbound)
{
byte[] transformedCookie = cookie;
if (Transforms == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4296)));
}
if (outbound)
{
for (int i = 0; i < _transforms.Count; i++)
{
transformedCookie = _transforms[i].Encode(transformedCookie);
}
}
else
{
for (int i = _transforms.Count; i > 0; i--)
{
transformedCookie = _transforms[i - 1].Decode(transformedCookie);
}
}
return transformedCookie;
}
///
/// Checks the reader if this is a SecurityContextToken.
///
/// XmlReader over the incoming SecurityToken.
/// 'True' if the reader points to a SecurityContextToken.
/// The input argument 'reader' is null.
public override bool CanReadToken(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
return (reader.IsStartElement(WSSecureConversationFeb2005Constants.ElementNames.Name, WSSecureConversationFeb2005Constants.Namespace)
|| reader.IsStartElement(WSSecureConversation13Constants.ElementNames.Name, WSSecureConversation13Constants.Namespace));
}
///
/// Indicates whether this handler supports validation of tokens.
///
/// 'True' if the class is capable of SecurityToken validation.
public override bool CanValidateToken
{
get { return true; }
}
///
/// Gets information on whether this Token Handler can write tokens.
///
public override bool CanWriteToken
{
get
{
return true;
}
}
///
/// Creates a security token based on a token descriptor.
///
/// The token descriptor.
/// A security token.
/// Thrown if 'tokenDescriptor' is null.
public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor)
{
if (null == tokenDescriptor)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
}
if (this.Configuration == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4272)));
}
ClaimsPrincipal principal = new ClaimsPrincipal(tokenDescriptor.Subject);
if (this.Configuration.SaveBootstrapContext)
{
SecurityTokenHandlerCollection bootstrapTokenCollection = CreateBootstrapTokenHandlerCollection();
if (!bootstrapTokenCollection.CanWriteToken(tokenDescriptor.Token))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4010, tokenDescriptor.Token.GetType().ToString())));
}
(principal.Identities as ReadOnlyCollection)[0].BootstrapContext = new BootstrapContext(tokenDescriptor.Token, bootstrapTokenCollection[tokenDescriptor.Token.GetType()]);
}
DateTime validFrom = (tokenDescriptor.Lifetime.Created.HasValue) ? (DateTime)tokenDescriptor.Lifetime.Created : DateTime.UtcNow;
DateTime validTo = (tokenDescriptor.Lifetime.Expires.HasValue) ? (DateTime)tokenDescriptor.Lifetime.Expires : DateTime.UtcNow + SessionSecurityTokenHandler.DefaultTokenLifetime;
return new SessionSecurityToken(principal, null, validFrom, validTo);
}
///
/// Creates a based on an and a valid time range.
///
///
/// Caller defined context string
/// Identifier of the endpoint to which the token is scoped.
/// Earliest valid time.
/// Latest valid time.
public virtual SessionSecurityToken CreateSessionSecurityToken(
ClaimsPrincipal principal,
string context,
string endpointId,
DateTime validFrom,
DateTime validTo)
{
if (null == principal)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("principal");
}
if (this.Configuration == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4272)));
}
return new SessionSecurityToken(principal, context, endpointId, validFrom, validTo);
}
///
/// Gets the default token lifetime
///
public static TimeSpan DefaultTokenLifetime
{
get { return DefaultLifetime; }
}
///
/// Reads the SessionSecurityToken from a stream of bytes.
///
/// token.
/// SecurityTokenResolver that can be used to resolve the SessionSecurityToken.
/// Instance of SessionSecurityToken.
public virtual SecurityToken ReadToken(byte[] token, SecurityTokenResolver tokenResolver)
{
// Our implementation of ReadToken( byte[] ) will always return null. We make the above call not to
// break SharePoint. SharePoint has overridden ReadToken(byte[] token) and expect the SessionAuthenticationModule to
// call that. So SessionAuthenticationModule will calls this method which does the correct thing.
using (XmlReader reader = XmlDictionaryReader.CreateTextReader(token, XmlDictionaryReaderQuotas.Max))
{
return this.ReadToken(reader, tokenResolver);
}
}
///
/// Reads the SessionSecurityToken from the given reader.
///
/// XmlReader over the SessionSecurityToken.
/// An instance of .
/// The input argument 'reader' is null.
/// The 'reader' is not positioned at a SessionSecurityToken
/// or the SessionSecurityToken cannot be read.
public override SecurityToken ReadToken(XmlReader reader)
{
return this.ReadToken(reader, EmptySecurityTokenResolver.Instance);
}
///
/// Reads the SessionSecurityToken from the given reader.
///
/// XmlReader over the SessionSecurityToken.
/// SecurityTokenResolver that can used to resolve SessionSecurityToken.
/// An instance of .
/// The input argument 'reader' is null.
/// The 'reader' is not positioned at a SessionSecurityToken
/// or the SessionSecurityToken cannot be read.
public override SecurityToken ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
if (tokenResolver == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenResolver");
}
byte[] encodedCookie = null;
SysUniqueId contextId = null;
SysUniqueId keyGeneration = null;
string ns = null;
string identifier = null;
string instance = null;
SecurityToken securityContextToken = null;
SessionDictionary dictionary = SessionDictionary.Instance;
XmlDictionaryReader dicReader = XmlDictionaryReader.CreateDictionaryReader(reader);
if (dicReader.IsStartElement(WSSecureConversationFeb2005Constants.ElementNames.Name, WSSecureConversationFeb2005Constants.Namespace))
{
ns = WSSecureConversationFeb2005Constants.Namespace;
identifier = WSSecureConversationFeb2005Constants.ElementNames.Identifier;
instance = WSSecureConversationFeb2005Constants.ElementNames.Instance;
}
else if (dicReader.IsStartElement(WSSecureConversation13Constants.ElementNames.Name, WSSecureConversation13Constants.Namespace))
{
ns = WSSecureConversation13Constants.Namespace;
identifier = WSSecureConversation13Constants.ElementNames.Identifier;
instance = WSSecureConversation13Constants.ElementNames.Instance;
}
else
{
//
// Something is wrong
//
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(
SR.GetString(SR.ID4230, WSSecureConversationFeb2005Constants.ElementNames.Name, dicReader.Name)));
}
string id = dicReader.GetAttribute(WSUtilityConstants.Attributes.IdAttribute, WSUtilityConstants.NamespaceURI);
dicReader.ReadFullStartElement();
if (!dicReader.IsStartElement(identifier, ns))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(
SR.GetString(SR.ID4230, WSSecureConversation13Constants.ElementNames.Identifier, dicReader.Name)));
}
contextId = dicReader.ReadElementContentAsUniqueId();
if (contextId == null || string.IsNullOrEmpty(contextId.ToString()))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4242)));
}
//
// The token can be a renewed token, in which case we need to know the
// instance id, which will be the secondary key to the context id for
// cache lookups
//
if (dicReader.IsStartElement(instance, ns))
{
keyGeneration = dicReader.ReadElementContentAsUniqueId();
}
if (dicReader.IsStartElement(CookieElementName, CookieNamespace))
{
// Get the token from the Cache, which is returned as an SCT
SecurityToken cachedToken = null;
SecurityContextKeyIdentifierClause sctClause = null;
if (keyGeneration == null)
{
sctClause = new SecurityContextKeyIdentifierClause(contextId);
}
else
{
sctClause = new SecurityContextKeyIdentifierClause(contextId, keyGeneration);
}
tokenResolver.TryResolveToken(sctClause, out cachedToken);
if (cachedToken != null)
{
securityContextToken = cachedToken;
dicReader.Skip();
}
else
{
//
// CookieMode
//
encodedCookie = dicReader.ReadElementContentAsBase64();
if (encodedCookie == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4237)));
}
//
// appply transforms
//
byte[] decodedCookie = ApplyTransforms(encodedCookie, false);
using (MemoryStream ms = new MemoryStream(decodedCookie))
{
BinaryFormatter formatter = new BinaryFormatter();
securityContextToken = formatter.Deserialize(ms) as SecurityToken;
}
SessionSecurityToken sessionToken = securityContextToken as SessionSecurityToken;
if (sessionToken != null && sessionToken.ContextId != contextId)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4229, sessionToken.ContextId, contextId)));
}
if (sessionToken != null && sessionToken.Id != id)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4227, sessionToken.Id, id)));
}
}
}
else
{
//
// SessionMode
//
// Get the token from the Cache.
SecurityToken cachedToken = null;
SecurityContextKeyIdentifierClause sctClause = null;
if (keyGeneration == null)
{
sctClause = new SecurityContextKeyIdentifierClause(contextId);
}
else
{
sctClause = new SecurityContextKeyIdentifierClause(contextId, keyGeneration);
}
tokenResolver.TryResolveToken(sctClause, out cachedToken);
if (cachedToken != null)
{
securityContextToken = cachedToken;
}
}
dicReader.ReadEndElement();
if (securityContextToken == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4243)));
}
return securityContextToken;
}
///
/// Gets or sets the TokenLifetime.
///
public virtual TimeSpan TokenLifetime
{
get { return _tokenLifetime; }
set
{
if (value <= TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.ID0016));
}
_tokenLifetime = value;
}
}
///
/// Gets the bootstrap token handler collection.
///
SecurityTokenHandlerCollection CreateBootstrapTokenHandlerCollection()
{
SecurityTokenHandlerCollection tokenHandlerCollection = this.ContainingCollection ?? SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
return tokenHandlerCollection;
}
///
/// Gets the token type URIs
///
public override string[] GetTokenTypeIdentifiers()
{
return new string[] { SecureConversationTokenIdentifier,
WSSecureConversation13Constants.TokenTypeURI,
WSSecureConversationFeb2005Constants.TokenTypeURI };
}
///
/// Gets the type of token this handler can work with.
///
public override Type TokenType
{
get { return typeof(SessionSecurityToken); }
}
///
/// Gets the transforms that will be applied to the cookie.
///
public ReadOnlyCollection Transforms
{
get
{
return _transforms;
}
}
///
/// Sets the transforms that will be applied to cookies.
///
/// The objects to use.
protected void SetTransforms(IEnumerable transforms)
{
_transforms = new List(transforms).AsReadOnly();
}
///
/// Validates a .
///
/// The to validate.
/// A of representing the identities contained in the token.
/// The parameter 'token' is null.
/// The token is not assignable from .
public override ReadOnlyCollection ValidateToken(SecurityToken token)
{
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SessionSecurityToken sessionToken = token as SessionSecurityToken;
if (sessionToken == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4292, token.GetType().ToString(), this.GetType().ToString())));
}
try
{
if (DiagnosticUtility.ShouldTrace(TraceEventType.Verbose))
{
TraceUtility.TraceEvent(
TraceEventType.Verbose,
TraceCode.Diagnostics,
SR.GetString(SR.TraceValidateToken),
new SecurityTraceRecordHelper.TokenTraceRecord(token),
null,
null);
}
this.ValidateSession(sessionToken);
this.TraceTokenValidationSuccess(token);
List identitites = new List(1);
identitites.AddRange(sessionToken.ClaimsPrincipal.Identities);
return identitites.AsReadOnly();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
this.TraceTokenValidationFailure(token, e.Message);
throw e;
}
}
///
/// Validates a token and returns its claims.
///
/// The to validate.
/// Identifier to the endpoint to which the token is scoped.
/// A of representing the identities contained in the token.
/// The parameter 'token' is null.
/// The parameter 'endpointId' is null.
/// token.EndpointId != endpointId.
public virtual ReadOnlyCollection ValidateToken(SessionSecurityToken token, string endpointId)
{
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
if (endpointId == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointId");
}
// We consider SessionTokens with String.Empty as the endpoint Id to be
// globally scoped tokens. This in insecure, we are allowing this only
// for compatibility with customers who have overriden SessionSecurityTokenHandler.
if (!string.IsNullOrEmpty(token.EndpointId))
{
if (token.EndpointId != endpointId)
{
string errorMessage = SR.GetString(SR.ID4291, token);
this.TraceTokenValidationFailure(token, errorMessage);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(errorMessage));
}
}
return this.ValidateToken(token);
}
///
/// Checks the valid time of a SecurityToken.
///
///
/// The token is invalid if the securityToken.ValidFrom > DateTime.UtcNow OR securityToken.ValidTo < DateTime.UtcNow
///
/// The to validate.
/// Thrown if 'securityToken' is null.
/// Thrown if 'Configuration' is null.
/// Thrown if securityToken.ValidFrom > DateTime.UtcNow.
/// Thrown if securityToken.ValidTo < DateTime.UtcNow.
protected virtual void ValidateSession(SessionSecurityToken securityToken)
{
if (securityToken == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityToken");
}
if (this.Configuration == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4274)));
}
Fx.Assert(this.Configuration != null, SR.GetString(SR.ID8027));
DateTime utcNow = DateTime.UtcNow;
// apply clock skew here.
DateTime maxTime = DateTimeUtil.Add(utcNow, Configuration.MaxClockSkew);
DateTime minTime = DateTimeUtil.Add(utcNow, -Configuration.MaxClockSkew);
if (securityToken.ValidFrom > maxTime)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenNotYetValidException(SR.GetString(SR.ID4255, securityToken.ValidTo, securityToken.ValidFrom, utcNow)));
}
if (securityToken.ValidTo < minTime)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenExpiredException(SR.GetString(SR.ID4255, securityToken.ValidTo, securityToken.ValidFrom, utcNow)));
}
}
///
/// Writes the token into a byte array.
///
/// The SessionSecurityToken to write.
/// Thrown if 'sessiontoken' is null.
/// An encoded byte array.
public virtual byte[] WriteToken(SessionSecurityToken sessionToken)
{
if (sessionToken == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sessionToken");
}
using (MemoryStream ms = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(ms))
{
WriteToken(writer, sessionToken);
writer.Flush();
}
return ms.ToArray();
}
}
///
/// Serializes the given token to the XmlWriter.
///
/// XmlWriter to which the token needs to be serialized
/// The SecurityToken to be serialized.
/// The input argument 'writer' is null.
/// The input argument 'token' is either null or not of type
/// SessionSecurityToken.
public override void WriteToken(XmlWriter writer, SecurityToken token)
{
if (writer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
}
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SessionSecurityToken sessionToken = token as SessionSecurityToken;
if (sessionToken == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4046, token, TokenType)));
}
string ns, elementName, contextIdElementName, instance;
if (sessionToken.SecureConversationVersion == WSSecureConversationFeb2005Constants.NamespaceUri)
{
ns = WSSecureConversationFeb2005Constants.Namespace;
elementName = WSSecureConversationFeb2005Constants.ElementNames.Name;
contextIdElementName = WSSecureConversationFeb2005Constants.ElementNames.Identifier;
instance = WSSecureConversationFeb2005Constants.ElementNames.Instance;
}
else if (sessionToken.SecureConversationVersion == WSSecureConversation13Constants.NamespaceUri)
{
ns = WSSecureConversation13Constants.Namespace;
elementName = WSSecureConversation13Constants.ElementNames.Name;
contextIdElementName = WSSecureConversation13Constants.ElementNames.Identifier;
instance = WSSecureConversation13Constants.ElementNames.Instance;
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4050)));
}
XmlDictionaryWriter dicWriter;
SessionDictionary dictionary = SessionDictionary.Instance;
if (writer is XmlDictionaryWriter)
{
dicWriter = (XmlDictionaryWriter)writer;
}
else
{
dicWriter = XmlDictionaryWriter.CreateDictionaryWriter(writer);
}
dicWriter.WriteStartElement(elementName, ns);
if (sessionToken.Id != null)
{
dicWriter.WriteAttributeString(WSUtilityConstants.Attributes.IdAttribute, WSUtilityConstants.NamespaceURI, sessionToken.Id);
}
dicWriter.WriteElementString(contextIdElementName, ns, sessionToken.ContextId.ToString());
if (sessionToken.KeyGeneration != null)
{
dicWriter.WriteStartElement(instance, ns);
dicWriter.WriteValue(sessionToken.KeyGeneration);
dicWriter.WriteEndElement();
}
if (!sessionToken.IsReferenceMode)
{
dicWriter.WriteStartElement(CookieElementName, CookieNamespace);
byte[] cookie;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, token);
cookie = ms.ToArray();
}
cookie = ApplyTransforms(cookie, true);
dicWriter.WriteBase64(cookie, 0, cookie.Length);
dicWriter.WriteEndElement();
}
dicWriter.WriteEndElement();
dicWriter.Flush();
}
}
}