File: System\ServiceModel\Security\Tokens\IssuedSecurityTokenProvider.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Security.Tokens
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.IdentityModel.Claims;
    using System.IdentityModel.Policy;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Security;
    using System.Xml;
 
    using SafeCloseHandle = System.IdentityModel.SafeCloseHandle;
    using SafeFreeCredentials = System.IdentityModel.SafeFreeCredentials;
    using SafeNativeMethods = System.ServiceModel.ComIntegration.SafeNativeMethods;
    using Win32Error = System.ServiceModel.ComIntegration.Win32Error;
    using WSTrustFeb2005Constants = System.IdentityModel.Protocols.WSTrust.WSTrustFeb2005Constants;
    using WSTrust13Constants = System.IdentityModel.Protocols.WSTrust.WSTrust13Constants;
    using WSTrust14Constants = System.IdentityModel.Protocols.WSTrust.WSTrust14Constants;
    using System.IO;
    using System.Text;
    
    public class IssuedSecurityTokenProvider : SecurityTokenProvider, ICommunicationObject
    {
        CoreFederatedTokenProvider federatedTokenProvider;
        MessageSecurityVersion messageSecurityVersion;
        SecurityTokenSerializer securityTokenSerializer;
        SecurityTokenHandlerCollectionManager tokenHandlerCollectionManager = null;
 
        public IssuedSecurityTokenProvider()
            : this(null)
        {
        }
 
        internal IssuedSecurityTokenProvider(SafeFreeCredentials credentialsHandle)
        {
            this.federatedTokenProvider = new CoreFederatedTokenProvider(credentialsHandle);
            this.messageSecurityVersion = MessageSecurityVersion.Default;
        }
 
        public event EventHandler Closed
        {
            add { this.federatedTokenProvider.Closed += value; }
            remove { this.federatedTokenProvider.Closed -= value; }
        }
 
        public event EventHandler Closing
        {
            add { this.federatedTokenProvider.Closing += value; }
            remove { this.federatedTokenProvider.Closing -= value; }
        }
 
        public event EventHandler Faulted
        {
            add { this.federatedTokenProvider.Faulted += value; }
            remove { this.federatedTokenProvider.Faulted -= value; }
        }
 
        public event EventHandler Opened
        {
            add { this.federatedTokenProvider.Opened += value; }
            remove { this.federatedTokenProvider.Opened -= value; }
        }
 
        public event EventHandler Opening
        {
            add { this.federatedTokenProvider.Opening += value; }
            remove { this.federatedTokenProvider.Opening -= value; }
        }
 
        public Binding IssuerBinding
        {
            get
            {
                return this.federatedTokenProvider.IssuerBinding;
            }
            set
            {
                this.federatedTokenProvider.IssuerBinding = value;
            }
        }
 
        public KeyedByTypeCollection<IEndpointBehavior> IssuerChannelBehaviors
        {
            get
            {
                return this.federatedTokenProvider.IssuerChannelBehaviors;
            }
        }
 
        public Collection<XmlElement> TokenRequestParameters
        {
            get
            {
                return this.federatedTokenProvider.RequestProperties;
            }
        }
 
        public EndpointAddress IssuerAddress
        {
            get
            {
                return this.federatedTokenProvider.IssuerAddress;
            }
            set
            {
                this.federatedTokenProvider.IssuerAddress = value;
            }
        }
 
        public EndpointAddress TargetAddress
        {
            get
            {
                return this.federatedTokenProvider.TargetAddress;
            }
            set
            {
                this.federatedTokenProvider.TargetAddress = value;
            }
        }
 
        public SecurityKeyEntropyMode KeyEntropyMode
        {
            get
            {
                return this.federatedTokenProvider.KeyEntropyMode;
            }
            set
            {
                this.federatedTokenProvider.KeyEntropyMode = value;
            }
        }
 
        public IdentityVerifier IdentityVerifier
        {
            get
            {
                return this.federatedTokenProvider.IdentityVerifier;
            }
            set
            {
                this.federatedTokenProvider.IdentityVerifier = value;
            }
        }
 
        public bool CacheIssuedTokens
        {
            get
            {
                return this.federatedTokenProvider.CacheServiceTokens;
            }
            set
            {
                this.federatedTokenProvider.CacheServiceTokens = value;
            }
        }
 
        public TimeSpan MaxIssuedTokenCachingTime
        {
            get
            {
                return this.federatedTokenProvider.MaxServiceTokenCachingTime;
            }
            set
            {
                this.federatedTokenProvider.MaxServiceTokenCachingTime = value;
            }
        }
 
        public MessageSecurityVersion MessageSecurityVersion
        {
            get
            {
                return this.messageSecurityVersion;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
                }
                this.messageSecurityVersion = value;
            }
        }
 
        public SecurityTokenSerializer SecurityTokenSerializer
        {
            get
            {
                return this.securityTokenSerializer;
            }
            set
            {
                this.securityTokenSerializer = value;
            }
        }
 
        public SecurityAlgorithmSuite SecurityAlgorithmSuite
        {
            get
            {
                return this.federatedTokenProvider.SecurityAlgorithmSuite;
            }
            set
            {
                this.federatedTokenProvider.SecurityAlgorithmSuite = value;
            }
        }
 
        public int IssuedTokenRenewalThresholdPercentage
        {
            get
            {
                return this.federatedTokenProvider.ServiceTokenValidityThresholdPercentage;
            }
            set
            {
                this.federatedTokenProvider.ServiceTokenValidityThresholdPercentage = value;
            }
        }
 
        public CommunicationState State
        {
            get { return this.federatedTokenProvider.State; }
        }
 
        public virtual TimeSpan DefaultOpenTimeout
        {
            get { return ServiceDefaults.OpenTimeout; }
        }
 
        public virtual TimeSpan DefaultCloseTimeout
        {
            get { return ServiceDefaults.CloseTimeout; }
        }
 
        public override bool SupportsTokenCancellation
        {
            get
            {
                return this.federatedTokenProvider.SupportsTokenCancellation;
            }
        }
 
        internal ChannelParameterCollection ChannelParameters
        {
            get
            {
                return this.federatedTokenProvider.ChannelParameters;
            }
            set
            {
                this.federatedTokenProvider.ChannelParameters = value;
            }
        }
 
        internal SecurityTokenHandlerCollectionManager TokenHandlerCollectionManager
        {
            get
            {
                return this.tokenHandlerCollectionManager;
            }
            set
            {
                this.tokenHandlerCollectionManager = value;
            }
        }
 
        // communication object methods
        public void Abort()
        {
            this.federatedTokenProvider.Abort();
        }
 
        public void Close()
        {
            this.federatedTokenProvider.Close();
        }
 
        public void Close(TimeSpan timeout)
        {
            this.federatedTokenProvider.Close(timeout);
        }
 
        public IAsyncResult BeginClose(AsyncCallback callback, object state)
        {
            return this.federatedTokenProvider.BeginClose(callback, state);
        }
 
        public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.federatedTokenProvider.BeginClose(timeout, callback, state);
        }
 
        public void EndClose(IAsyncResult result)
        {
            this.federatedTokenProvider.EndClose(result);
        }
 
        void OnOpenCore()
        {
            if (this.securityTokenSerializer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TokenSerializerNotSetonFederationProvider)));
            }
            this.federatedTokenProvider.StandardsManager = new SecurityStandardsManager(this.messageSecurityVersion, this.securityTokenSerializer);
        }
 
        public void Open()
        {
            OnOpenCore();
 
            this.federatedTokenProvider.Open();
        }
 
        public void Open(TimeSpan timeout)
        {
            OnOpenCore();
            this.federatedTokenProvider.Open(timeout);
        }
 
        public IAsyncResult BeginOpen(AsyncCallback callback, object state)
        {
            OnOpenCore();
            return this.federatedTokenProvider.BeginOpen(callback, state);
        }
 
        public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            OnOpenCore();
            return this.federatedTokenProvider.BeginOpen(timeout, callback, state);
        }
 
        public void EndOpen(IAsyncResult result)
        {
            this.federatedTokenProvider.EndOpen(result);
        }
 
        public void Dispose()
        {
            this.Close();
        }
 
        // token provider methods
 
        protected override IAsyncResult BeginGetTokenCore(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.federatedTokenProvider.BeginGetToken(timeout, callback, state);
        }
 
        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            return this.federatedTokenProvider.GetToken(timeout);
        }
 
        protected override SecurityToken EndGetTokenCore(IAsyncResult result)
        {
            return this.federatedTokenProvider.EndGetToken(result);
        }
 
        internal void SetupActAsOnBehalfOfParameters(System.IdentityModel.Protocols.WSTrust.FederatedClientCredentialsParameters actAsOnBehalfOfParameters)
        {
            if (actAsOnBehalfOfParameters == null)
                return;
 
            if (actAsOnBehalfOfParameters.IssuedSecurityToken != null)
            {
                throw System.IdentityModel.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.AuthFailed));
            }
 
            if (actAsOnBehalfOfParameters.OnBehalfOf != null)
            {
                if (MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13)
                {
                    if (TokenRequestParameterExists(WSTrust13Constants.ElementNames.OnBehalfOf, WSTrust13Constants.NamespaceURI))
                    {
                        throw System.IdentityModel.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.DuplicateFederatedClientCredentialsParameters, WSTrust13Constants.ElementNames.OnBehalfOf));
                    }
 
                    TokenRequestParameters.Add(CreateXmlTokenElement(actAsOnBehalfOfParameters.OnBehalfOf,
                                                                   WSTrust13Constants.Prefix,
                                                                   WSTrust13Constants.ElementNames.OnBehalfOf,
                                                                   WSTrust13Constants.NamespaceURI,
                                                                   SecurityTokenHandlerCollectionManager.Usage.OnBehalfOf));
 
                }
                else if (MessageSecurityVersion.TrustVersion == TrustVersion.WSTrustFeb2005)
                {
                    if (TokenRequestParameterExists(WSTrustFeb2005Constants.ElementNames.OnBehalfOf, WSTrustFeb2005Constants.NamespaceURI))
                    {
                        throw System.IdentityModel.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.DuplicateFederatedClientCredentialsParameters, WSTrustFeb2005Constants.ElementNames.OnBehalfOf));
                    }
 
                    TokenRequestParameters.Add(CreateXmlTokenElement(actAsOnBehalfOfParameters.OnBehalfOf,
                                                                   WSTrustFeb2005Constants.Prefix,
                                                                   WSTrustFeb2005Constants.ElementNames.OnBehalfOf,
                                                                   WSTrustFeb2005Constants.NamespaceURI,
                                                                   SecurityTokenHandlerCollectionManager.Usage.OnBehalfOf));
 
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedTrustVersion, MessageSecurityVersion.TrustVersion.Namespace)));
                }
            }
            if (actAsOnBehalfOfParameters.ActAs != null)
            {
                if (TokenRequestParameterExists(WSTrust14Constants.ElementNames.ActAs, WSTrust14Constants.NamespaceURI))
                {
                    throw System.IdentityModel.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.DuplicateFederatedClientCredentialsParameters, WSTrust14Constants.ElementNames.ActAs));
                }
 
                TokenRequestParameters.Add(CreateXmlTokenElement(actAsOnBehalfOfParameters.ActAs,
                                                                   WSTrust14Constants.Prefix,
                                                                   WSTrust14Constants.ElementNames.ActAs,
                                                                   WSTrust14Constants.NamespaceURI,
                                                                   SecurityTokenHandlerCollectionManager.Usage.ActAs));
            }
        }
 
        bool TokenRequestParameterExists(string localName, string xmlNamespace)
        {
            foreach (XmlElement element in TokenRequestParameters)
            {
                if (element.LocalName == localName &&
                    element.NamespaceURI == xmlNamespace)
                {
                    return true;
                }
            }
            return false;
        }
 
        XmlElement CreateXmlTokenElement(SecurityToken token, string prefix, string name, string ns, string usage)
        {
            Stream stream = new MemoryStream();
 
            using (XmlDictionaryWriter xmlWriter = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false))
            {
                xmlWriter.WriteStartElement(prefix, name, ns);
                WriteToken(xmlWriter, token, usage);
                xmlWriter.WriteEndElement();
                xmlWriter.Flush();
            }
 
            stream.Seek(0, SeekOrigin.Begin);
 
            XmlDocument dom = new XmlDocument();
            dom.PreserveWhitespace = true;
            dom.Load(new XmlTextReader(stream) { DtdProcessing = DtdProcessing.Prohibit });
            stream.Close();
 
            return dom.DocumentElement;
        }
 
        void WriteToken(XmlWriter xmlWriter, SecurityToken token, string usage)
        {
            SecurityTokenHandlerCollection tokenHandlerCollection = null;
            if (this.tokenHandlerCollectionManager.ContainsKey(usage))
            {
                tokenHandlerCollection = this.tokenHandlerCollectionManager[usage];
            }
            else
            {
                tokenHandlerCollection = this.tokenHandlerCollectionManager[SecurityTokenHandlerCollectionManager.Usage.Default];
            }
 
            if (tokenHandlerCollection != null && tokenHandlerCollection.CanWriteToken(token))
            {
                tokenHandlerCollection.WriteToken(xmlWriter, token);
            }
            else
            {
                SecurityTokenSerializer.WriteToken(xmlWriter, token);
            }
        }
 
        private class CoreFederatedTokenProvider : IssuanceTokenProviderBase<FederatedTokenProviderState>
        {
            internal const SecurityKeyEntropyMode defaultKeyEntropyMode = SecurityKeyEntropyMode.CombinedEntropy;
            static int MaxRsaSecurityTokenCacheSize = 1024;
            IChannelFactory<IRequestChannel> channelFactory;
            Binding issuerBinding;
            KeyedByTypeCollection<IEndpointBehavior> channelBehaviors;
            Collection<XmlElement> requestProperties = new Collection<XmlElement>();
            IdentityVerifier identityVerifier = IdentityVerifier.CreateDefault();
            bool addTargetServiceAppliesTo;
            SecurityKeyEntropyMode keyEntropyMode;
            SecurityKeyType keyType;
            bool isKeyTypePresentInRstProperties;
            int keySize;
            bool isKeySizePresentInRstProperties;
            int defaultPublicKeySize = 1024;
            MessageVersion messageVersion;
            ChannelParameterCollection channelParameters;
            readonly List<RsaSecurityToken> rsaSecurityTokens = new List<RsaSecurityToken>();
            SafeFreeCredentials credentialsHandle;
            bool ownCredentialsHandle;
 
            public CoreFederatedTokenProvider(SafeFreeCredentials credentialsHandle) : base()
            {
                this.credentialsHandle = credentialsHandle;
                this.channelBehaviors = new KeyedByTypeCollection<IEndpointBehavior>();
                this.addTargetServiceAppliesTo = true;
                this.keyEntropyMode = defaultKeyEntropyMode;
            }
 
            public Binding IssuerBinding
            {
                get
                {
                    return this.issuerBinding;
                }
                set
                {
                    this.CommunicationObject.ThrowIfDisposedOrImmutable();
                    this.issuerBinding = value;
                }
            }
 
            public Collection<XmlElement> RequestProperties
            {
                get
                {
                    return this.requestProperties;
                }
            }
 
            public SecurityKeyEntropyMode KeyEntropyMode
            {
                get
                {
                    return this.keyEntropyMode;
                }
                set
                {
                    this.CommunicationObject.ThrowIfDisposedOrImmutable();
                    SecurityKeyEntropyModeHelper.Validate(value);
                    this.keyEntropyMode = value;
                }
            }
 
            public IdentityVerifier IdentityVerifier
            {
                get
                {
                    return this.identityVerifier;
                }
                set
                {
                    this.CommunicationObject.ThrowIfDisposedOrImmutable();
                    this.identityVerifier = value;
                }
            }
 
            public ChannelParameterCollection ChannelParameters
            {
                get
                {
                    return this.channelParameters;
                }
                set
                {
                    this.CommunicationObject.ThrowIfDisposedOrImmutable();
                    this.channelParameters = value;
                }
            }
 
            public KeyedByTypeCollection<IEndpointBehavior> IssuerChannelBehaviors
            {
                get
                {
                    return this.channelBehaviors;
                }
            }
 
            public override XmlDictionaryString RequestSecurityTokenAction
            {
                get
                {
                    return this.StandardsManager.TrustDriver.RequestSecurityTokenAction;
                }
            }
 
            public override XmlDictionaryString RequestSecurityTokenResponseAction
            {
                get
                {
                    return this.StandardsManager.TrustDriver.RequestSecurityTokenResponseAction;
                }
            }
 
 
            protected override MessageVersion MessageVersion
            {
                get
                {
                    return this.messageVersion;
                }
            }
 
            protected override bool RequiresManualReplyAddressing
            {
                get 
                {
                    // the proxy adds reply headers automatically
                    return false;
                }
            }
 
            bool TryGetKeyType(out SecurityKeyType keyType)
            {
                if (this.requestProperties != null)
                {
                    for (int i = 0; i < this.requestProperties.Count; ++i)
                    {
                        if (this.StandardsManager.TrustDriver.TryParseKeyTypeElement(this.requestProperties[i], out keyType))
                        {
                            return true;
                        }
                    }
                }
                keyType = SecurityKeyType.SymmetricKey;
                return false;
            }
 
            bool TryGetKeySize(out int keySize)
            {
                if (this.requestProperties != null)
                {
                    for (int i = 0; i < this.requestProperties.Count; ++i)
                    {
                        if (this.StandardsManager.TrustDriver.TryParseKeySizeElement(this.requestProperties[i], out keySize))
                        {
                            return true;
                        }
                    }
                }
                keySize = 0;
                return false;
            }
 
            public override void OnOpen(TimeSpan timeout)
            {
                if (this.IssuerAddress == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.StsAddressNotSet, this.TargetAddress)));
                }
                if (this.IssuerBinding == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.StsBindingNotSet, this.IssuerAddress)));
                }
                if (this.SecurityAlgorithmSuite == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityAlgorithmSuiteNotSet, typeof(IssuedSecurityTokenProvider))));
                }
                this.channelFactory = this.StandardsManager.TrustDriver.CreateFederationProxy(this.IssuerAddress, this.IssuerBinding, this.IssuerChannelBehaviors);
                this.messageVersion = this.IssuerBinding.MessageVersion;
 
                // if an appliesTo is specified in the request properties, then do not add the target service EPR as
                // appliesTo
                for (int i = 0; i < this.requestProperties.Count; ++i)
                {
                    if (this.StandardsManager.TrustDriver.IsAppliesTo(this.requestProperties[i].LocalName, this.requestProperties[i].NamespaceURI))
                    {
                        this.addTargetServiceAppliesTo = false;
                        break;
                    }
                }
                this.isKeyTypePresentInRstProperties = TryGetKeyType(out this.keyType);
                if (!this.isKeyTypePresentInRstProperties)
                {
                    this.keyType = SecurityKeyType.SymmetricKey;
                }
                this.isKeySizePresentInRstProperties = TryGetKeySize(out this.keySize);
                if (!this.isKeySizePresentInRstProperties && this.keyType != SecurityKeyType.BearerKey)
                {
                    this.keySize = (this.keyType == SecurityKeyType.SymmetricKey) ? this.SecurityAlgorithmSuite.DefaultSymmetricKeyLength : this.defaultPublicKeySize;
                }
 
                base.OnOpen(timeout);
            }
 
            public override void OnOpening()
            {
                base.OnOpening();
                if (this.credentialsHandle == null)
                {
                    if (this.IssuerBinding == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.StsBindingNotSet, this.IssuerAddress)));
                    }
                    this.credentialsHandle = SecurityUtils.GetCredentialsHandle(this.IssuerBinding, this.IssuerChannelBehaviors);
                    this.ownCredentialsHandle = true;
                }
            }
 
            public override void OnAbort()
            {
                if (this.channelFactory != null && this.channelFactory.State == CommunicationState.Opened)
                {
                    this.channelFactory.Abort();
                    this.channelFactory = null;
                }
                CleanUpRsaSecurityTokenCache();
                FreeCredentialsHandle();
                base.OnAbort();
            }
 
            public override void OnClose(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                if (this.channelFactory != null && this.channelFactory.State == CommunicationState.Opened)
                {
                    this.channelFactory.Close(timeoutHelper.RemainingTime());
                    this.channelFactory = null;
                    CleanUpRsaSecurityTokenCache();
                    FreeCredentialsHandle();
                    base.OnClose(timeoutHelper.RemainingTime());
                }
            }
 
            void FreeCredentialsHandle()
            {
                if (this.credentialsHandle != null)
                {
                    if (this.ownCredentialsHandle)
                    {
                        this.credentialsHandle.Close();
                    }
                    this.credentialsHandle = null;
                }
            }
 
            protected override bool WillInitializeChannelFactoriesCompleteSynchronously(EndpointAddress target)
            {
                return (this.channelFactory.State != CommunicationState.Opened);
            }
 
            protected override void InitializeChannelFactories(EndpointAddress target, TimeSpan timeout)
            {
                if (this.channelFactory.State == CommunicationState.Created)
                {
                    this.channelFactory.Open(timeout); 
                }
            }
 
            protected override IAsyncResult BeginInitializeChannelFactories(EndpointAddress target, TimeSpan timeout, AsyncCallback callback, object state)
            {
                if (this.channelFactory.State == CommunicationState.Created)
                {
                    return this.channelFactory.BeginOpen(timeout, callback, state); 
                }
                else
                {
                    return new CompletedAsyncResult(callback, state);
                }
            }
 
            protected override void EndInitializeChannelFactories(IAsyncResult result)
            {
                if (result is CompletedAsyncResult)
                {
                    CompletedAsyncResult.End(result);
                }
                else
                {
                    this.channelFactory.EndOpen(result);
                }
            }
 
            protected override IRequestChannel CreateClientChannel(EndpointAddress target, Uri via)
            {
                IRequestChannel result = this.channelFactory.CreateChannel(this.IssuerAddress);
                if (this.channelParameters != null)
                {
                    this.channelParameters.PropagateChannelParameters(result);
                }
                if (this.ownCredentialsHandle)
                {
                    ChannelParameterCollection newParameters = result.GetProperty<ChannelParameterCollection>();
                    if (newParameters != null)
                    {
                        newParameters.Add(new SspiIssuanceChannelParameter(true, this.credentialsHandle));
                    }
                }
                ReplaceSspiIssuanceChannelParameter(result.GetProperty<ChannelParameterCollection>(), new SspiIssuanceChannelParameter(true, this.credentialsHandle));
 
                return result;
            }
 
            void ReplaceSspiIssuanceChannelParameter( ChannelParameterCollection channelParameters, SspiIssuanceChannelParameter sicp )
            {
                if (channelParameters != null)
                {
                    for (int i = 0; i < channelParameters.Count; ++i)
                    {
                        if (channelParameters[i] is SspiIssuanceChannelParameter)
                        {
                            channelParameters.RemoveAt(i);
                        }
                    }
 
                    channelParameters.Add(sicp);
                }
            }
 
            protected override bool CreateNegotiationStateCompletesSynchronously(EndpointAddress target, Uri via)
            {
                return true;
            }
 
            protected override IAsyncResult BeginCreateNegotiationState(EndpointAddress target, Uri via, TimeSpan timeout, AsyncCallback callback, object state)
            {
                return new CompletedAsyncResult<FederatedTokenProviderState>(this.CreateNegotiationState(target, via, timeout), callback, state);
            }
 
            protected override FederatedTokenProviderState EndCreateNegotiationState(IAsyncResult result)
            {
                return CompletedAsyncResult<FederatedTokenProviderState>.End(result);
            }
 
            protected override FederatedTokenProviderState CreateNegotiationState(EndpointAddress target, Uri via, TimeSpan timeout)
            {
                if ((this.keyType == SecurityKeyType.SymmetricKey) || (this.keyType == SecurityKeyType.BearerKey))
                {
                    byte[] keyEntropy;
                    if (this.KeyEntropyMode == SecurityKeyEntropyMode.CombinedEntropy || this.KeyEntropyMode == SecurityKeyEntropyMode.ClientEntropy)
                    {
                        keyEntropy = new byte[this.keySize / 8];
                        CryptoHelper.FillRandomBytes(keyEntropy);
                    }
                    else
                    {
                        keyEntropy = null;
                    }
                    return new FederatedTokenProviderState(keyEntropy);
                }
                else if (this.keyType == SecurityKeyType.AsymmetricKey)
                {
                    return new FederatedTokenProviderState(CreateAndCacheRsaSecurityToken());
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
                }
            }
 
            protected override BodyWriter GetFirstOutgoingMessageBody(FederatedTokenProviderState negotiationState, out MessageProperties messageProperties)
            {
                messageProperties = null;
                RequestSecurityToken rst = new RequestSecurityToken(this.StandardsManager);
                if (this.addTargetServiceAppliesTo)
                {
                    if (this.MessageVersion.Addressing == AddressingVersion.WSAddressing10)
                    {
                        rst.SetAppliesTo<EndpointAddress10>(
                            EndpointAddress10.FromEndpointAddress(negotiationState.TargetAddress),
                            DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddress10), DataContractSerializerDefaults.MaxItemsInObjectGraph));
                    }
                    else if (this.MessageVersion.Addressing == AddressingVersion.WSAddressingAugust2004)
                    {
                        rst.SetAppliesTo<EndpointAddressAugust2004>(
                            EndpointAddressAugust2004.FromEndpointAddress(negotiationState.TargetAddress),
                            DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddressAugust2004), DataContractSerializerDefaults.MaxItemsInObjectGraph));
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, this.MessageVersion.Addressing)));
                    }
                }
                rst.Context = negotiationState.Context;
                if (!this.isKeySizePresentInRstProperties)
                {
                    rst.KeySize = this.keySize;
                }
                Collection<XmlElement> newRequestProperties = new Collection<XmlElement>();
                if (this.requestProperties != null)
                {
                    for (int i = 0; i < this.requestProperties.Count; ++i)
                    {
                        newRequestProperties.Add(this.requestProperties[i]);
                    }
                }
                if (!isKeyTypePresentInRstProperties)
                {
                    XmlElement keyTypeElement = this.StandardsManager.TrustDriver.CreateKeyTypeElement(this.keyType);
                    newRequestProperties.Insert(0, keyTypeElement);
                }
                if (this.keyType == SecurityKeyType.SymmetricKey)
                {
                    byte[] requestorEntropy = negotiationState.GetRequestorEntropy();
                    rst.SetRequestorEntropy(requestorEntropy);
                }
                else if (this.keyType == SecurityKeyType.AsymmetricKey)
                {
                    RsaKeyIdentifierClause rsaClause = new RsaKeyIdentifierClause(negotiationState.Rsa);
                    SecurityKeyIdentifier keyIdentifier = new SecurityKeyIdentifier(rsaClause);
                    newRequestProperties.Add(this.StandardsManager.TrustDriver.CreateUseKeyElement(keyIdentifier, this.StandardsManager));
                    RsaSecurityTokenParameters rsaParameters = new RsaSecurityTokenParameters();
                    rsaParameters.InclusionMode = SecurityTokenInclusionMode.Never;
                    rsaParameters.RequireDerivedKeys = false;
                    SupportingTokenSpecification rsaSpec = new SupportingTokenSpecification(negotiationState.RsaSecurityToken, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, SecurityTokenAttachmentMode.Endorsing, rsaParameters);
                    messageProperties = new MessageProperties();
                    SecurityMessageProperty security = new SecurityMessageProperty();
                    security.OutgoingSupportingTokens.Add(rsaSpec);
                    messageProperties.Security = security;
                }
                if (this.keyType == SecurityKeyType.SymmetricKey && this.KeyEntropyMode == SecurityKeyEntropyMode.CombinedEntropy)
                {
                    newRequestProperties.Add(this.StandardsManager.TrustDriver.CreateComputedKeyAlgorithmElement(this.StandardsManager.TrustDriver.ComputedKeyAlgorithm));
                }
                rst.RequestProperties = newRequestProperties;
                rst.MakeReadOnly();
                return rst;
            }
 
            protected ReadOnlyCollection<IAuthorizationPolicy> GetServiceAuthorizationPolicies(AcceleratedTokenProviderState negotiationState)
            {
                EndpointIdentity identity;
                if (this.identityVerifier.TryGetIdentity(negotiationState.TargetAddress, out identity))
                {
                    List<Claim> claims = new List<Claim>(1);
                    claims.Add(identity.IdentityClaim);
 
                    List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
                    policies.Add(new UnconditionalPolicy(SecurityUtils.CreateIdentity(identity.IdentityClaim.Resource.ToString()), 
                            new DefaultClaimSet(ClaimSet.System, claims)));
                    return policies.AsReadOnly();
                }
                else
                {
                    return EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance;
                }
            }
 
            protected override BodyWriter GetNextOutgoingMessageBody(Message incomingMessage, FederatedTokenProviderState negotiationState)
            {
                ThrowIfFault(incomingMessage, this.IssuerAddress);
                if ((this.StandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrustFeb2005 && incomingMessage.Headers.Action != this.StandardsManager.TrustDriver.RequestSecurityTokenResponseAction.Value) ||
                    (this.StandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13 && incomingMessage.Headers.Action != this.StandardsManager.TrustDriver.RequestSecurityTokenResponseFinalAction.Value) ||
                    incomingMessage.Headers.Action == null)
                {
                    throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.InvalidActionForNegotiationMessage, incomingMessage.Headers.Action)), incomingMessage);
                }
                RequestSecurityTokenResponse rstr = null;
                XmlDictionaryReader bodyReader = incomingMessage.GetReaderAtBodyContents();
                using (bodyReader)
                {
                    if (this.StandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrustFeb2005)
                        rstr = this.StandardsManager.TrustDriver.CreateRequestSecurityTokenResponse(bodyReader);
                    else if (this.StandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13)
                    {
                        RequestSecurityTokenResponseCollection rstrc = this.StandardsManager.TrustDriver.CreateRequestSecurityTokenResponseCollection(bodyReader);
                        foreach (RequestSecurityTokenResponse rstrItem in rstrc.RstrCollection)
                        {
                            if (rstr != null)
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MoreThanOneRSTRInRSTRC)));
                            rstr = rstrItem;
                        }
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
                    }
 
                    incomingMessage.ReadFromBodyContentsToEnd(bodyReader);
                }
                if (rstr.Context != negotiationState.Context)
                {
                    throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.BadSecurityNegotiationContext)), incomingMessage);
                }
                GenericXmlSecurityToken serviceToken;
                if ((this.keyType == SecurityKeyType.SymmetricKey) ||
                    (this.keyType == SecurityKeyType.BearerKey))
                {
                    ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = GetServiceAuthorizationPolicies(negotiationState);
                    byte[] keyEntropy = negotiationState.GetRequestorEntropy();
                    serviceToken = rstr.GetIssuedToken(null, null, this.KeyEntropyMode, keyEntropy, null, authorizationPolicies, this.keySize, this.keyType == SecurityKeyType.BearerKey);
                }
                else if (this.keyType == SecurityKeyType.AsymmetricKey)
                {
                    serviceToken = rstr.GetIssuedToken(null, EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance, negotiationState.Rsa);
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
                }
                negotiationState.SetServiceToken(serviceToken);
                return null;
            }
 
            // SC/Trust workshop change to turn off context
            protected override bool IsMultiLegNegotiation
            {
                get { return false; }
            }
 
            // This is to address RSACryptoServiceProvider finalizer exception issue
            // Step 1. Create Rsa and force deterministic keypair gen in this calling context.
            // Step 2. Cache if the calling thread is under impersonation context.  The cache will 
            // be disposed on Close/Abort assuming same calling context thread as the one calling open.
            RsaSecurityToken CreateAndCacheRsaSecurityToken()
            {
                RsaSecurityToken token;
                // Cache only under impersonation context.
                // 1) set cacheSize less than 0, to ignore this new behavior at all.
                // 2) set cacheSize to 0, if token provider should not dispose issued tokens on close/abort.
                // 3) other than that, the token provider will track and dispose issued tokens as much.
                if (MaxRsaSecurityTokenCacheSize >= 0 && IsImpersonatedContext())
                {
                    // This will force deterministic keypair gen in this context.
                    token = RsaSecurityToken.CreateSafeRsaSecurityToken(this.keySize);
                    if (MaxRsaSecurityTokenCacheSize > 0)
                    {
                        lock (this.rsaSecurityTokens)
                        {
                            // Remove/Dispose the first token if cache is full.
                            // The first token (if not disposed) will rely on GC for finalization.
                            if (this.rsaSecurityTokens.Count >= MaxRsaSecurityTokenCacheSize)
                            {
                                this.rsaSecurityTokens.RemoveAt(0);
                            }
                            this.rsaSecurityTokens.Add(token);
                        }
                    }
                }
                else
                {
                    token = new RsaSecurityToken(new RSACryptoServiceProvider(this.keySize));
                }
                return token;
            }
 
            void CleanUpRsaSecurityTokenCache()
            {
                lock (this.rsaSecurityTokens)
                {
                    for (int i = 0; i < this.rsaSecurityTokens.Count; ++i)
                    {
                        this.rsaSecurityTokens[i].Dispose();
                    }
                    this.rsaSecurityTokens.Clear();
                }
            }
 
            // This api simply check if the calling thread is process primary thread.
            // We are not trying to be smart if the impersonation to the same user as 
            // process token since privileges could be different.
            bool IsImpersonatedContext()
            {
                SafeCloseHandle tokenHandle = null;
                if (!SafeNativeMethods.OpenCurrentThreadToken(
                                SafeNativeMethods.GetCurrentThread(),
                                TokenAccessLevels.Query,
                                true,
                                out tokenHandle))
                {
                    int error = Marshal.GetLastWin32Error();
                    Utility.CloseInvalidOutSafeHandle(tokenHandle);
                    if (error == (int)Win32Error.ERROR_NO_TOKEN)
                    {
                        return false;
                    }
                    System.ServiceModel.Dispatcher.ErrorBehavior.ThrowAndCatch(new Win32Exception(error));
                    return true;
                }
                tokenHandle.Close();
                return true;
            }
 
            protected override void ValidateKeySize(GenericXmlSecurityToken issuedToken)
            {
                if (this.keyType == SecurityKeyType.BearerKey)
                {
                    // We do not have a proof key associated with bearer 
                    // key type. So skip key size validation.
                    return;
                }
                base.ValidateKeySize(issuedToken);
            }
 
        }
 
        class FederatedTokenProviderState : AcceleratedTokenProviderState
        {
            RsaSecurityToken rsaToken;
 
            public FederatedTokenProviderState(byte[] entropy)
                : base(entropy)
            {
            }
 
            public FederatedTokenProviderState(RsaSecurityToken rsaToken)
                : base(null)
            {
                this.rsaToken = rsaToken;
            }
 
            public RSA Rsa
            {
                get { return this.rsaToken.Rsa; }
            }
 
            public RsaSecurityToken RsaSecurityToken
            {
                get { return this.rsaToken; }
            }
        }
 
    }
}