File: System\ServiceModel\Security\ClientCredentialsSecurityTokenManager.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel
{
    using System.Collections.Generic;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.Net;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Security;
    using System.ServiceModel.Security.Tokens;
    using System.Xml;
    using System.IdentityModel.Protocols.WSTrust;
    using SafeFreeCredentials = System.IdentityModel.SafeFreeCredentials;
 
    public class ClientCredentialsSecurityTokenManager : SecurityTokenManager
    {
        ClientCredentials parent;
 
        public ClientCredentialsSecurityTokenManager(ClientCredentials clientCredentials)
        {
            if (clientCredentials == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("clientCredentials");
            }
            this.parent = clientCredentials;
        }
 
        public ClientCredentials ClientCredentials
        {
            get { return this.parent; }
        }
 
        string GetServicePrincipalName(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            EndpointAddress targetAddress = initiatorRequirement.TargetAddress;
            if (targetAddress == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenRequirementDoesNotSpecifyTargetAddress, initiatorRequirement));
            }
            IdentityVerifier identityVerifier;
            SecurityBindingElement securityBindingElement = initiatorRequirement.SecurityBindingElement;
            if (securityBindingElement != null)
            {
                identityVerifier = securityBindingElement.LocalClientSettings.IdentityVerifier;
            }
            else
            {
                identityVerifier = IdentityVerifier.CreateDefault();
            }
            EndpointIdentity identity;
            identityVerifier.TryGetIdentity(targetAddress, out identity);
            return SecurityUtils.GetSpnFromIdentity(identity, targetAddress);
        }
 
        SspiSecurityToken GetSpnegoClientCredential(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            InitiatorServiceModelSecurityTokenRequirement sspiCredentialRequirement = new InitiatorServiceModelSecurityTokenRequirement();
            sspiCredentialRequirement.TargetAddress = initiatorRequirement.TargetAddress;
            sspiCredentialRequirement.TokenType = ServiceModelSecurityTokenTypes.SspiCredential;
            sspiCredentialRequirement.Via = initiatorRequirement.Via;
            sspiCredentialRequirement.RequireCryptographicToken = false;
            sspiCredentialRequirement.SecurityBindingElement = initiatorRequirement.SecurityBindingElement;
            sspiCredentialRequirement.MessageSecurityVersion = initiatorRequirement.MessageSecurityVersion;
            ChannelParameterCollection parameters;
            if (initiatorRequirement.TryGetProperty<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, out parameters))
            {
                sspiCredentialRequirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = parameters;
            }
            SecurityTokenProvider sspiTokenProvider = this.CreateSecurityTokenProvider(sspiCredentialRequirement);
            SecurityUtils.OpenTokenProviderIfRequired(sspiTokenProvider, TimeSpan.Zero);
            SspiSecurityToken sspiToken = (SspiSecurityToken) sspiTokenProvider.GetToken(TimeSpan.Zero);
            SecurityUtils.AbortTokenProviderIfRequired(sspiTokenProvider);
            return sspiToken;
        }
 
        SecurityTokenProvider CreateSpnegoTokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            EndpointAddress targetAddress = initiatorRequirement.TargetAddress;
            if (targetAddress == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenRequirementDoesNotSpecifyTargetAddress, initiatorRequirement));
            }
            SecurityBindingElement securityBindingElement = initiatorRequirement.SecurityBindingElement;
            if (securityBindingElement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenProviderRequiresSecurityBindingElement, initiatorRequirement));
            }
            SspiIssuanceChannelParameter sspiChannelParameter = GetSspiIssuanceChannelParameter(initiatorRequirement);
            bool negotiateTokenOnOpen = (sspiChannelParameter == null ? true : sspiChannelParameter.GetTokenOnOpen);
            LocalClientSecuritySettings localClientSettings = securityBindingElement.LocalClientSettings;
            BindingContext issuerBindingContext = initiatorRequirement.GetProperty<BindingContext>(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty);
            SpnegoTokenProvider spnegoTokenProvider = new SpnegoTokenProvider(sspiChannelParameter != null ? sspiChannelParameter.CredentialsHandle : null, securityBindingElement);
            SspiSecurityToken clientSspiToken = GetSpnegoClientCredential(initiatorRequirement);
            spnegoTokenProvider.ClientCredential = clientSspiToken.NetworkCredential;
            spnegoTokenProvider.IssuerAddress = initiatorRequirement.IssuerAddress;
            spnegoTokenProvider.AllowedImpersonationLevel = parent.Windows.AllowedImpersonationLevel;
            spnegoTokenProvider.AllowNtlm = clientSspiToken.AllowNtlm;
            spnegoTokenProvider.IdentityVerifier = localClientSettings.IdentityVerifier;
            spnegoTokenProvider.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
            // if this is not a supporting token, authenticate the server
            spnegoTokenProvider.AuthenticateServer = !initiatorRequirement.Properties.ContainsKey(ServiceModelSecurityTokenRequirement.SupportingTokenAttachmentModeProperty);
            spnegoTokenProvider.NegotiateTokenOnOpen = negotiateTokenOnOpen;
            spnegoTokenProvider.CacheServiceTokens = negotiateTokenOnOpen || localClientSettings.CacheCookies;
            spnegoTokenProvider.IssuerBindingContext = issuerBindingContext;
            spnegoTokenProvider.MaxServiceTokenCachingTime = localClientSettings.MaxCookieCachingTime;
            spnegoTokenProvider.ServiceTokenValidityThresholdPercentage = localClientSettings.CookieRenewalThresholdPercentage;
            spnegoTokenProvider.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(initiatorRequirement, this);
            spnegoTokenProvider.TargetAddress = targetAddress;
            spnegoTokenProvider.Via = initiatorRequirement.GetPropertyOrDefault<Uri>(InitiatorServiceModelSecurityTokenRequirement.ViaProperty, null);
            spnegoTokenProvider.ApplicationProtectionRequirements = (issuerBindingContext != null) ? issuerBindingContext.BindingParameters.Find<ChannelProtectionRequirements>() : null;
            spnegoTokenProvider.InteractiveNegoExLogonEnabled = this.ClientCredentials.SupportInteractive;
            
            return spnegoTokenProvider;
        }
 
        SecurityTokenProvider CreateTlsnegoClientX509TokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            InitiatorServiceModelSecurityTokenRequirement clientX509Requirement = new InitiatorServiceModelSecurityTokenRequirement();
            clientX509Requirement.TokenType = SecurityTokenTypes.X509Certificate;
            clientX509Requirement.TargetAddress = initiatorRequirement.TargetAddress;
            clientX509Requirement.SecurityBindingElement = initiatorRequirement.SecurityBindingElement;
            clientX509Requirement.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
            clientX509Requirement.RequireCryptographicToken = true;
            clientX509Requirement.MessageSecurityVersion = initiatorRequirement.MessageSecurityVersion;
            clientX509Requirement.KeyUsage = SecurityKeyUsage.Signature;
            clientX509Requirement.KeyType = SecurityKeyType.AsymmetricKey;
            clientX509Requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Output;
            ChannelParameterCollection parameters;
            if (initiatorRequirement.TryGetProperty<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, out parameters))
            {
                clientX509Requirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = parameters;
            }
            return this.CreateSecurityTokenProvider(clientX509Requirement);
        }
 
        SecurityTokenAuthenticator CreateTlsnegoServerX509TokenAuthenticator(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            InitiatorServiceModelSecurityTokenRequirement serverX509Requirement = new InitiatorServiceModelSecurityTokenRequirement();
            serverX509Requirement.TokenType = SecurityTokenTypes.X509Certificate;
            serverX509Requirement.RequireCryptographicToken = true;
            serverX509Requirement.SecurityBindingElement = initiatorRequirement.SecurityBindingElement;
            serverX509Requirement.MessageSecurityVersion = initiatorRequirement.MessageSecurityVersion;
            serverX509Requirement.KeyUsage = SecurityKeyUsage.Exchange;
            serverX509Requirement.KeyType = SecurityKeyType.AsymmetricKey;
            serverX509Requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Input;
            ChannelParameterCollection parameters;
            if (initiatorRequirement.TryGetProperty<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, out parameters))
            {
                serverX509Requirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = parameters;
            }
            SecurityTokenResolver dummy;
            return this.CreateSecurityTokenAuthenticator(serverX509Requirement, out dummy);
        }
 
        SspiIssuanceChannelParameter GetSspiIssuanceChannelParameter(SecurityTokenRequirement initiatorRequirement)
        {
            ChannelParameterCollection channelParameters;
            if (initiatorRequirement.TryGetProperty<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, out channelParameters))
            {
                if (channelParameters != null)
                {
                    for (int i = 0; i < channelParameters.Count; ++i)
                    {
                        if (channelParameters[i] is SspiIssuanceChannelParameter)
                        {
                            return (SspiIssuanceChannelParameter)channelParameters[i];
                        }
                    }
                }
            }
            return null;
        }
 
        SecurityTokenProvider CreateTlsnegoTokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement, bool requireClientCertificate)
        {
            EndpointAddress targetAddress = initiatorRequirement.TargetAddress;
            if (targetAddress == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenRequirementDoesNotSpecifyTargetAddress, initiatorRequirement));
            }
            SecurityBindingElement securityBindingElement = initiatorRequirement.SecurityBindingElement;
            if (securityBindingElement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenProviderRequiresSecurityBindingElement, initiatorRequirement));
            }
            SspiIssuanceChannelParameter sspiChannelParameter = GetSspiIssuanceChannelParameter(initiatorRequirement);
            bool negotiateTokenOnOpen = sspiChannelParameter != null && sspiChannelParameter.GetTokenOnOpen;
            LocalClientSecuritySettings localClientSettings = securityBindingElement.LocalClientSettings;
            BindingContext issuerBindingContext = initiatorRequirement.GetProperty<BindingContext>(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty);
            TlsnegoTokenProvider tlsnegoTokenProvider = new TlsnegoTokenProvider();
            tlsnegoTokenProvider.IssuerAddress = initiatorRequirement.IssuerAddress;
            tlsnegoTokenProvider.NegotiateTokenOnOpen = negotiateTokenOnOpen;
            tlsnegoTokenProvider.CacheServiceTokens = negotiateTokenOnOpen || localClientSettings.CacheCookies;
            if (requireClientCertificate)
            {
                tlsnegoTokenProvider.ClientTokenProvider = this.CreateTlsnegoClientX509TokenProvider(initiatorRequirement);
            }
            tlsnegoTokenProvider.IssuerBindingContext = issuerBindingContext;
            tlsnegoTokenProvider.ApplicationProtectionRequirements = (issuerBindingContext != null) ? issuerBindingContext.BindingParameters.Find<ChannelProtectionRequirements>() : null;
            tlsnegoTokenProvider.MaxServiceTokenCachingTime = localClientSettings.MaxCookieCachingTime;
            tlsnegoTokenProvider.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
            tlsnegoTokenProvider.ServerTokenAuthenticator = this.CreateTlsnegoServerX509TokenAuthenticator(initiatorRequirement);
            tlsnegoTokenProvider.ServiceTokenValidityThresholdPercentage = localClientSettings.CookieRenewalThresholdPercentage;
            tlsnegoTokenProvider.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(initiatorRequirement, this);
            tlsnegoTokenProvider.TargetAddress = initiatorRequirement.TargetAddress;
            tlsnegoTokenProvider.Via = initiatorRequirement.GetPropertyOrDefault<Uri>(InitiatorServiceModelSecurityTokenRequirement.ViaProperty, null);
            return tlsnegoTokenProvider;
        }
 
        SecurityTokenProvider CreateSecureConversationSecurityTokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            EndpointAddress targetAddress = initiatorRequirement.TargetAddress;
            if (targetAddress == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenRequirementDoesNotSpecifyTargetAddress, initiatorRequirement));
            }
            SecurityBindingElement securityBindingElement = initiatorRequirement.SecurityBindingElement;
            if (securityBindingElement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenProviderRequiresSecurityBindingElement, initiatorRequirement));
            }
            LocalClientSecuritySettings localClientSettings = securityBindingElement.LocalClientSettings;
            BindingContext issuerBindingContext = initiatorRequirement.GetProperty<BindingContext>(ServiceModelSecurityTokenRequirement.IssuerBindingContextProperty);
            ChannelParameterCollection channelParameters = initiatorRequirement.GetPropertyOrDefault<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, null);
            bool isSessionMode = initiatorRequirement.SupportSecurityContextCancellation;
            if (isSessionMode)
            {
                SecuritySessionSecurityTokenProvider sessionTokenProvider = new SecuritySessionSecurityTokenProvider(GetCredentialsHandle(initiatorRequirement));
                sessionTokenProvider.BootstrapSecurityBindingElement = SecurityUtils.GetIssuerSecurityBindingElement(initiatorRequirement);
                sessionTokenProvider.IssuedSecurityTokenParameters = initiatorRequirement.GetProperty<SecurityTokenParameters>(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty);
                sessionTokenProvider.IssuerBindingContext = issuerBindingContext;
                sessionTokenProvider.KeyEntropyMode = securityBindingElement.KeyEntropyMode;
                sessionTokenProvider.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
                sessionTokenProvider.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(initiatorRequirement, this);
                sessionTokenProvider.TargetAddress = targetAddress;
                sessionTokenProvider.Via = initiatorRequirement.GetPropertyOrDefault<Uri>(InitiatorServiceModelSecurityTokenRequirement.ViaProperty, null);
                Uri privacyNoticeUri;
                if (initiatorRequirement.TryGetProperty<Uri>(ServiceModelSecurityTokenRequirement.PrivacyNoticeUriProperty, out privacyNoticeUri))
                {
                    sessionTokenProvider.PrivacyNoticeUri = privacyNoticeUri;
                }
                int privacyNoticeVersion;
                if (initiatorRequirement.TryGetProperty<int>(ServiceModelSecurityTokenRequirement.PrivacyNoticeVersionProperty, out privacyNoticeVersion))
                {
                    sessionTokenProvider.PrivacyNoticeVersion = privacyNoticeVersion;
                }
                EndpointAddress localAddress;
                if (initiatorRequirement.TryGetProperty<EndpointAddress>(ServiceModelSecurityTokenRequirement.DuplexClientLocalAddressProperty, out localAddress))
                {
                    sessionTokenProvider.LocalAddress = localAddress;
                }
                sessionTokenProvider.ChannelParameters = channelParameters;
                sessionTokenProvider.WebHeaders = initiatorRequirement.WebHeaders;
 
                return sessionTokenProvider;
            }
            else
            {
                AcceleratedTokenProvider acceleratedTokenProvider = new AcceleratedTokenProvider(GetCredentialsHandle(initiatorRequirement));
                acceleratedTokenProvider.IssuerAddress = initiatorRequirement.IssuerAddress;
                acceleratedTokenProvider.BootstrapSecurityBindingElement = SecurityUtils.GetIssuerSecurityBindingElement(initiatorRequirement);
                acceleratedTokenProvider.CacheServiceTokens = localClientSettings.CacheCookies;
                acceleratedTokenProvider.IssuerBindingContext = issuerBindingContext;
                acceleratedTokenProvider.KeyEntropyMode = securityBindingElement.KeyEntropyMode;
                acceleratedTokenProvider.MaxServiceTokenCachingTime = localClientSettings.MaxCookieCachingTime;
                acceleratedTokenProvider.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
                acceleratedTokenProvider.ServiceTokenValidityThresholdPercentage = localClientSettings.CookieRenewalThresholdPercentage;
                acceleratedTokenProvider.StandardsManager = SecurityUtils.CreateSecurityStandardsManager(initiatorRequirement, this);
                acceleratedTokenProvider.TargetAddress = targetAddress;
                acceleratedTokenProvider.Via = initiatorRequirement.GetPropertyOrDefault<Uri>(InitiatorServiceModelSecurityTokenRequirement.ViaProperty, null);
                Uri privacyNoticeUri;
                if (initiatorRequirement.TryGetProperty<Uri>(ServiceModelSecurityTokenRequirement.PrivacyNoticeUriProperty, out privacyNoticeUri))
                {
                    acceleratedTokenProvider.PrivacyNoticeUri = privacyNoticeUri;
                }
                acceleratedTokenProvider.ChannelParameters = channelParameters;
                int privacyNoticeVersion;
                if (initiatorRequirement.TryGetProperty<int>(ServiceModelSecurityTokenRequirement.PrivacyNoticeVersionProperty, out privacyNoticeVersion))
                {
                    acceleratedTokenProvider.PrivacyNoticeVersion = privacyNoticeVersion;
                }
                return acceleratedTokenProvider;
            }
        }
 
        SecurityTokenProvider CreateServerX509TokenProvider(EndpointAddress targetAddress)
        {
            X509Certificate2 targetServerCertificate = null;
            if (targetAddress != null)
            {
                parent.ServiceCertificate.ScopedCertificates.TryGetValue(targetAddress.Uri, out targetServerCertificate);
            }
            if (targetServerCertificate == null)
            {
                targetServerCertificate = parent.ServiceCertificate.DefaultCertificate;
            }
            if ((targetServerCertificate == null) && (targetAddress.Identity != null) && (targetAddress.Identity.GetType() == typeof(X509CertificateEndpointIdentity)))
            {
                targetServerCertificate = ((X509CertificateEndpointIdentity)targetAddress.Identity).Certificates[0];
            }            
            if (targetServerCertificate != null)
            {
                return new X509SecurityTokenProvider(targetServerCertificate);
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ServiceCertificateNotProvidedOnClientCredentials, targetAddress.Uri)));
            }
        }
 
        X509SecurityTokenAuthenticator CreateServerX509TokenAuthenticator()
        {
            return new X509SecurityTokenAuthenticator(parent.ServiceCertificate.Authentication.GetCertificateValidator(), false);
        }
 
        X509SecurityTokenAuthenticator CreateServerSslX509TokenAuthenticator()
        {
            if (parent.ServiceCertificate.SslCertificateAuthentication != null)
            {
                return new X509SecurityTokenAuthenticator(parent.ServiceCertificate.SslCertificateAuthentication.GetCertificateValidator(), false);
            }
 
            return CreateServerX509TokenAuthenticator();
        }
 
        bool IsDigestAuthenticationScheme(SecurityTokenRequirement requirement)
        {
            if (requirement.Properties.ContainsKey(ServiceModelSecurityTokenRequirement.HttpAuthenticationSchemeProperty))
            {
                AuthenticationSchemes authScheme = (AuthenticationSchemes)requirement.Properties[ServiceModelSecurityTokenRequirement.HttpAuthenticationSchemeProperty];
 
                if (!authScheme.IsSingleton())
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.HttpRequiresSingleAuthScheme, authScheme));
                }
 
                return (authScheme == AuthenticationSchemes.Digest);
            }
            else
            {
                return false;
            }
        }
 
        internal protected bool IsIssuedSecurityTokenRequirement(SecurityTokenRequirement requirement)
        {
            if (requirement != null && requirement.Properties.ContainsKey(ServiceModelSecurityTokenRequirement.IssuerAddressProperty))
            {
                // handle all issued token requirements except for spnego, tlsnego and secure conversation
                if (requirement.TokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego || requirement.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego
                    || requirement.TokenType == ServiceModelSecurityTokenTypes.SecureConversation || requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            return false;
        }
 
        void CopyIssuerChannelBehaviorsAndAddSecurityCredentials(IssuedSecurityTokenProvider federationTokenProvider, KeyedByTypeCollection<IEndpointBehavior> issuerChannelBehaviors, EndpointAddress issuerAddress)
        {
            if (issuerChannelBehaviors != null)
            {
                foreach (IEndpointBehavior behavior in issuerChannelBehaviors)
                {
                    if (behavior is SecurityCredentialsManager)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuerChannelBehaviorsCannotContainSecurityCredentialsManager, issuerAddress, typeof(SecurityCredentialsManager))));
                    }
                    federationTokenProvider.IssuerChannelBehaviors.Add(behavior);
                }
            }
            federationTokenProvider.IssuerChannelBehaviors.Add(parent);
        }
 
        SecurityKeyEntropyMode GetIssuerBindingKeyEntropyModeOrDefault(Binding issuerBinding)
        {
            BindingElementCollection bindingElements = issuerBinding.CreateBindingElements();
            SecurityBindingElement securityBindingElement = bindingElements.Find<SecurityBindingElement>();
            if (securityBindingElement != null)
            {
                return securityBindingElement.KeyEntropyMode;
            }
            else
            {
                return parent.IssuedToken.DefaultKeyEntropyMode;
            }
        }
 
        void GetIssuerBindingSecurityVersion(Binding issuerBinding, MessageSecurityVersion issuedTokenParametersDefaultMessageSecurityVersion, SecurityBindingElement outerSecurityBindingElement, out MessageSecurityVersion messageSecurityVersion, out SecurityTokenSerializer tokenSerializer)
        {
            // Logic for setting version is:
            // 1. use issuer SBE
            // 2. use ITSP
            // 3. use outer SBE
            //
 
            messageSecurityVersion = null;
 
            if (issuerBinding != null)
            {
                BindingElementCollection bindingElements = issuerBinding.CreateBindingElements();
                SecurityBindingElement securityBindingElement = bindingElements.Find<SecurityBindingElement>();
                if (securityBindingElement != null)
                {
                    messageSecurityVersion = securityBindingElement.MessageSecurityVersion;
                }
            }
 
            if (messageSecurityVersion == null)
            {
                if (issuedTokenParametersDefaultMessageSecurityVersion != null)
                {
                    messageSecurityVersion = issuedTokenParametersDefaultMessageSecurityVersion;
                }
                else if (outerSecurityBindingElement != null)
                {
                    messageSecurityVersion = outerSecurityBindingElement.MessageSecurityVersion;
                }
            }
 
            if (messageSecurityVersion == null)
            {
                messageSecurityVersion = MessageSecurityVersion.Default;
            }
 
            tokenSerializer = this.CreateSecurityTokenSerializer(messageSecurityVersion.SecurityTokenVersion);
        }
 
        IssuedSecurityTokenProvider CreateIssuedSecurityTokenProvider(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement, FederatedClientCredentialsParameters actAsOnBehalfOfParameters)
        {
            if (initiatorRequirement.TargetAddress == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenRequirementDoesNotSpecifyTargetAddress, initiatorRequirement));
            }
            SecurityBindingElement securityBindingElement = initiatorRequirement.SecurityBindingElement;
            if (securityBindingElement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.TokenProviderRequiresSecurityBindingElement, initiatorRequirement));
            }
 
            EndpointAddress issuerAddress = initiatorRequirement.IssuerAddress;
            Binding issuerBinding = initiatorRequirement.IssuerBinding;
 
            //
            // If the issuer address is indeed anonymous or null, we will try the local issuer
            //
            bool isLocalIssuer = (issuerAddress == null || issuerAddress.Equals(EndpointAddress.AnonymousAddress));
 
            if (isLocalIssuer)
            {
                issuerAddress = parent.IssuedToken.LocalIssuerAddress;
                issuerBinding = parent.IssuedToken.LocalIssuerBinding;
            }
            if (issuerAddress == null)
            {
                // if issuer address is still null then the user forgot to specify the local issuer
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.StsAddressNotSet, initiatorRequirement.TargetAddress)));
            }
            if (issuerBinding == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.StsBindingNotSet, issuerAddress)));
            }
 
            Uri issuerUri = issuerAddress.Uri;
            KeyedByTypeCollection<IEndpointBehavior> issuerChannelBehaviors;
            if (!parent.IssuedToken.IssuerChannelBehaviors.TryGetValue(issuerAddress.Uri, out issuerChannelBehaviors) && isLocalIssuer)
            {
                issuerChannelBehaviors = parent.IssuedToken.LocalIssuerChannelBehaviors;
            }
 
            IssuedSecurityTokenProvider federationTokenProvider = new IssuedSecurityTokenProvider(GetCredentialsHandle(initiatorRequirement));
            federationTokenProvider.TokenHandlerCollectionManager = this.parent.SecurityTokenHandlerCollectionManager;
            federationTokenProvider.TargetAddress = initiatorRequirement.TargetAddress;
            CopyIssuerChannelBehaviorsAndAddSecurityCredentials(federationTokenProvider, issuerChannelBehaviors, issuerAddress);
            federationTokenProvider.CacheIssuedTokens = parent.IssuedToken.CacheIssuedTokens;
            federationTokenProvider.IdentityVerifier = securityBindingElement.LocalClientSettings.IdentityVerifier;
            federationTokenProvider.IssuerAddress = issuerAddress;
            federationTokenProvider.IssuerBinding = issuerBinding;
            federationTokenProvider.KeyEntropyMode = GetIssuerBindingKeyEntropyModeOrDefault(issuerBinding);
            federationTokenProvider.MaxIssuedTokenCachingTime = parent.IssuedToken.MaxIssuedTokenCachingTime;
            federationTokenProvider.SecurityAlgorithmSuite = initiatorRequirement.SecurityAlgorithmSuite;
            MessageSecurityVersion issuerSecurityVersion;
            SecurityTokenSerializer issuerSecurityTokenSerializer;
            IssuedSecurityTokenParameters issuedTokenParameters = initiatorRequirement.GetProperty<IssuedSecurityTokenParameters>(ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty);
 
            GetIssuerBindingSecurityVersion(issuerBinding, issuedTokenParameters.DefaultMessageSecurityVersion, initiatorRequirement.SecurityBindingElement, out issuerSecurityVersion, out issuerSecurityTokenSerializer);
            federationTokenProvider.MessageSecurityVersion = issuerSecurityVersion;
            federationTokenProvider.SecurityTokenSerializer = issuerSecurityTokenSerializer;
            federationTokenProvider.IssuedTokenRenewalThresholdPercentage = parent.IssuedToken.IssuedTokenRenewalThresholdPercentage;
 
            IEnumerable<XmlElement> tokenRequestParameters = issuedTokenParameters.CreateRequestParameters(issuerSecurityVersion, issuerSecurityTokenSerializer);
            if (tokenRequestParameters != null)
            {
                foreach (XmlElement requestParameter in tokenRequestParameters)
                {
                    federationTokenProvider.TokenRequestParameters.Add(requestParameter);
                }
            }
            ChannelParameterCollection channelParameters;
            if (initiatorRequirement.TryGetProperty<ChannelParameterCollection>(ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty, out channelParameters))
            {
                federationTokenProvider.ChannelParameters = channelParameters;
            }
 
            federationTokenProvider.SetupActAsOnBehalfOfParameters(actAsOnBehalfOfParameters);
            return federationTokenProvider;
        }
 
        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            return this.CreateSecurityTokenProvider(tokenRequirement, false);
        }
 
        internal SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement, bool disableInfoCard)
        {
            if (tokenRequirement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenRequirement");
            }
 
            SecurityTokenProvider result = null;
            if (disableInfoCard || !CardSpaceTryCreateSecurityTokenProviderStub(tokenRequirement, this, out result))
            {
                if (tokenRequirement is RecipientServiceModelSecurityTokenRequirement && tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate && tokenRequirement.KeyUsage == SecurityKeyUsage.Exchange)
                {
                    // this is the uncorrelated duplex case
                    if (parent.ClientCertificate.Certificate == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ClientCertificateNotProvidedOnClientCredentials)));
                    }
                    result = new X509SecurityTokenProvider(parent.ClientCertificate.Certificate);
                }
                else if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement)
                {
                    InitiatorServiceModelSecurityTokenRequirement initiatorRequirement = tokenRequirement as InitiatorServiceModelSecurityTokenRequirement;
#pragma warning suppress 56506 // initiatorRequirement will never be null due to the preceding 'is' validation.
                    string tokenType = initiatorRequirement.TokenType;
                    if (IsIssuedSecurityTokenRequirement(initiatorRequirement))
                    {
                        FederatedClientCredentialsParameters additionalParameters = this.FindFederatedChannelParameters(tokenRequirement);
 
                        if (additionalParameters != null && additionalParameters.IssuedSecurityToken != null)
                        {
                            return new SimpleSecurityTokenProvider(additionalParameters.IssuedSecurityToken, tokenRequirement);
                        }
                        
                        result = CreateIssuedSecurityTokenProvider(initiatorRequirement, additionalParameters);
                    }
                    else if (tokenType == SecurityTokenTypes.X509Certificate)
                    {
                        if (initiatorRequirement.Properties.ContainsKey(SecurityTokenRequirement.KeyUsageProperty) && initiatorRequirement.KeyUsage == SecurityKeyUsage.Exchange)
                        {
                            result = CreateServerX509TokenProvider(initiatorRequirement.TargetAddress);
                        }
                        else
                        {
                            if (parent.ClientCertificate.Certificate == null)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ClientCertificateNotProvidedOnClientCredentials)));
                            }
                            result = new X509SecurityTokenProvider(parent.ClientCertificate.Certificate);
                        }
                    }
                    else if (tokenType == SecurityTokenTypes.Kerberos)
                    {
                        string spn = GetServicePrincipalName(initiatorRequirement);
                        result = new KerberosSecurityTokenProviderWrapper(
                            new KerberosSecurityTokenProvider(spn, parent.Windows.AllowedImpersonationLevel, SecurityUtils.GetNetworkCredentialOrDefault(parent.Windows.ClientCredential)), 
                            GetCredentialsHandle(initiatorRequirement));
                    }
                    else if (tokenType == SecurityTokenTypes.UserName)
                    {
                        if (parent.UserName.UserName == null )
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UserNamePasswordNotProvidedOnClientCredentials)));
                        }
                        result = new UserNameSecurityTokenProvider(parent.UserName.UserName, parent.UserName.Password);
                    }
                    else if (tokenType == ServiceModelSecurityTokenTypes.SspiCredential)
                    {
                        if (IsDigestAuthenticationScheme(initiatorRequirement))
                        {
                            result = new SspiSecurityTokenProvider(SecurityUtils.GetNetworkCredentialOrDefault(parent.HttpDigest.ClientCredential), true, parent.HttpDigest.AllowedImpersonationLevel);
                        }
                        else
                        {
 
 #pragma warning disable 618   // to disable AllowNtlm obsolete wanring.      
  
                            result = new SspiSecurityTokenProvider(SecurityUtils.GetNetworkCredentialOrDefault(parent.Windows.ClientCredential), 
                      
                                parent.Windows.AllowNtlm, 
                                parent.Windows.AllowedImpersonationLevel);
 #pragma warning restore 618 
                   
                        }
                    }
                    else if (tokenType == ServiceModelSecurityTokenTypes.Spnego)
                    {
                        result = CreateSpnegoTokenProvider(initiatorRequirement);
                    }
                    else if (tokenType == ServiceModelSecurityTokenTypes.MutualSslnego)
                    {
                        result = CreateTlsnegoTokenProvider(initiatorRequirement, true);
                    }
                    else if (tokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego)
                    {
                        result = CreateTlsnegoTokenProvider(initiatorRequirement, false);
                    }
                    else if (tokenType == ServiceModelSecurityTokenTypes.SecureConversation)
                    {
                        result = CreateSecureConversationSecurityTokenProvider(initiatorRequirement);
                    }
                }
            }
 
            if ((result == null) && !tokenRequirement.IsOptionalToken)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityTokenManagerCannotCreateProviderForRequirement, tokenRequirement)));
            }
 
            return result;
        }
 
        bool CardSpaceTryCreateSecurityTokenProviderStub(SecurityTokenRequirement tokenRequirement, ClientCredentialsSecurityTokenManager clientCredentialsTokenManager, out SecurityTokenProvider provider)
        {
            return InfoCardHelper.TryCreateSecurityTokenProvider(tokenRequirement, clientCredentialsTokenManager, out provider);
        }
 
        protected SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityVersion version)
        {
            if (version == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("version"));
            }
            return this.CreateSecurityTokenSerializer(MessageSecurityTokenVersion.GetSecurityTokenVersion(version, true));
        }
 
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            if (version == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version");
            }
 
            if (this.parent != null && this.parent.UseIdentityConfiguration)
            {
                return this.WrapTokenHandlersAsSecurityTokenSerializer(version);
            }
 
            MessageSecurityTokenVersion wsVersion = version as MessageSecurityTokenVersion;
            if (wsVersion != null)
            {
                return new WSSecurityTokenSerializer(wsVersion.SecurityVersion, wsVersion.TrustVersion, wsVersion.SecureConversationVersion, wsVersion.EmitBspRequiredAttributes, null, null, null);
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityTokenManagerCannotCreateSerializerForVersion, version)));
            }
        }
 
        private SecurityTokenSerializer WrapTokenHandlersAsSecurityTokenSerializer(SecurityTokenVersion version)
        {
            TrustVersion trustVersion = TrustVersion.WSTrust13;
            SecureConversationVersion scVersion = SecureConversationVersion.WSSecureConversation13;
            SecurityVersion securityVersion = SecurityVersion.WSSecurity11;
            foreach (string securitySpecification in version.GetSecuritySpecifications())
            {
                if (StringComparer.Ordinal.Equals(securitySpecification, WSTrustFeb2005Constants.NamespaceURI))
                {
                    trustVersion = TrustVersion.WSTrustFeb2005;
                }
                else if (StringComparer.Ordinal.Equals(securitySpecification, WSTrust13Constants.NamespaceURI))
                {
                    trustVersion = TrustVersion.WSTrust13;
                }
                else if (StringComparer.Ordinal.Equals(securitySpecification, System.IdentityModel.WSSecureConversationFeb2005Constants.Namespace))
                {
                    scVersion = SecureConversationVersion.WSSecureConversationFeb2005;
                }
                else if (StringComparer.Ordinal.Equals(securitySpecification, System.IdentityModel.WSSecureConversation13Constants.Namespace))
                {
                    scVersion = SecureConversationVersion.WSSecureConversation13;
                }
            }
 
            securityVersion = FederatedSecurityTokenManager.GetSecurityVersion(version);
 
            //
            // 
 
 
            SecurityTokenHandlerCollectionManager sthcm = this.parent.SecurityTokenHandlerCollectionManager;
            WsSecurityTokenSerializerAdapter adapter = new WsSecurityTokenSerializerAdapter(sthcm[SecurityTokenHandlerCollectionManager.Usage.Default], securityVersion, trustVersion, scVersion, false, null, null, null);
            return adapter;
        }
 
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenRequirement");
            }
 
            outOfBandTokenResolver = null;
            SecurityTokenAuthenticator result = null;
 
            InitiatorServiceModelSecurityTokenRequirement initiatorRequirement = tokenRequirement as InitiatorServiceModelSecurityTokenRequirement;
            if (initiatorRequirement != null)
            {
                string tokenType = initiatorRequirement.TokenType;
                if (IsIssuedSecurityTokenRequirement(initiatorRequirement))
                {
                    return new GenericXmlSecurityTokenAuthenticator();
                }
                else if (tokenType == SecurityTokenTypes.X509Certificate)
                {
                    if (initiatorRequirement.IsOutOfBandToken)
                    {
                        // when the client side soap security asks for a token authenticator, its for doing
                        // identity checks on the out of band server certificate
                        result = new X509SecurityTokenAuthenticator(X509CertificateValidator.None);
                    }
                    else if (initiatorRequirement.PreferSslCertificateAuthenticator)
                    {
                        result = CreateServerSslX509TokenAuthenticator();
                    }
                    else
                    {
                        result = CreateServerX509TokenAuthenticator();
                    }
                }
                else if (tokenType == SecurityTokenTypes.Rsa)
                {
                    result = new RsaSecurityTokenAuthenticator();
                }
                else if (tokenType == SecurityTokenTypes.Kerberos)
                {
                    result = new KerberosRequestorSecurityTokenAuthenticator();
                }
                else if (tokenType == ServiceModelSecurityTokenTypes.SecureConversation
                    || tokenType == ServiceModelSecurityTokenTypes.MutualSslnego
                    || tokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego
                    || tokenType == ServiceModelSecurityTokenTypes.Spnego)
                {
                    result = new GenericXmlSecurityTokenAuthenticator();
                }
            }
            else if ((tokenRequirement is RecipientServiceModelSecurityTokenRequirement) && tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
            {
                // uncorrelated duplex case
                result = CreateServerX509TokenAuthenticator();
            }
 
            if (result == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityTokenManagerCannotCreateAuthenticatorForRequirement, tokenRequirement)));
            }
 
            return result;
        }
 
        SafeFreeCredentials GetCredentialsHandle(InitiatorServiceModelSecurityTokenRequirement initiatorRequirement)
        {
            SspiIssuanceChannelParameter sspiChannelParameter = GetSspiIssuanceChannelParameter(initiatorRequirement);
            return sspiChannelParameter != null ? sspiChannelParameter.CredentialsHandle : null;
        }
 
        /// <summary>
        /// Looks for the first FederatedClientCredentialsParameters object in the ChannelParameterCollection
        /// property on the tokenRequirement.
        /// </summary>
        internal FederatedClientCredentialsParameters FindFederatedChannelParameters(SecurityTokenRequirement tokenRequirement)
        {
            FederatedClientCredentialsParameters issuedTokenClientCredentialsParameters = null;
 
            ChannelParameterCollection channelParameterCollection = null;
            if (tokenRequirement.TryGetProperty<ChannelParameterCollection>(
                                     ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty,
                                     out channelParameterCollection))
            {
                if (channelParameterCollection != null)
                {
                    foreach (object obj in channelParameterCollection)
                    {
                        issuedTokenClientCredentialsParameters = obj as FederatedClientCredentialsParameters;
                        if (issuedTokenClientCredentialsParameters != null)
                        {
                            break;
                        }
                    }
                }
            }
            return issuedTokenClientCredentialsParameters;
        }
 
        internal class KerberosSecurityTokenProviderWrapper : CommunicationObjectSecurityTokenProvider
        {
            KerberosSecurityTokenProvider innerProvider;
            SafeFreeCredentials credentialsHandle;
            bool ownCredentialsHandle = false;
            
            public KerberosSecurityTokenProviderWrapper(KerberosSecurityTokenProvider innerProvider, SafeFreeCredentials credentialsHandle)
            {
                this.innerProvider = innerProvider;
                this.credentialsHandle = credentialsHandle;
            }
 
            public override void OnOpening()
            {
                base.OnOpening();
                if (this.credentialsHandle == null)
                {
                    this.credentialsHandle = SecurityUtils.GetCredentialsHandle("Kerberos", this.innerProvider.NetworkCredential, false);
                    this.ownCredentialsHandle = true;
                }
            }
 
            public override void OnClose(TimeSpan timeout)
            {
                base.OnClose(timeout);
                FreeCredentialsHandle();
            }
 
            public override void OnAbort()
            {
                base.OnAbort();
                FreeCredentialsHandle();
            }
 
            void FreeCredentialsHandle()
            {
                if (this.credentialsHandle != null)
                {
                    if (this.ownCredentialsHandle)
                    {
                        this.credentialsHandle.Close();
                    }
                    this.credentialsHandle = null;
                }
            }
 
            internal SecurityToken GetToken(TimeSpan timeout, ChannelBinding channelbinding)
            {
                return new KerberosRequestorSecurityToken(this.innerProvider.ServicePrincipalName, 
                    this.innerProvider.TokenImpersonationLevel, this.innerProvider.NetworkCredential,
                    SecurityUniqueId.Create().Value, this.credentialsHandle, channelbinding);
            }
            protected override SecurityToken GetTokenCore(TimeSpan timeout)
            {
                return this.GetToken(timeout, null);
            }
        }
    }
}